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
qstylekitstyle.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// Qt-Security score:significant reason:default
4
7#include <QtWidgets/qstyleoption.h>
8#include <QtWidgets/qstyle.h>
9#include <QtWidgets/qapplication.h>
10#if QT_CONFIG(scrollarea)
11# include <QtWidgets/qabstractscrollarea.h>
12#endif
13#if QT_CONFIG(itemviews)
14# include <QtWidgets/qabstractitemview.h>
15#endif
16#if QT_CONFIG(lineedit)
17# include <QtWidgets/qlineedit.h>
18#endif
19#if QT_CONFIG(pushbutton)
20# include <QtWidgets/qpushbutton.h>
21#endif
22#if QT_CONFIG(checkbox)
23# include <QtWidgets/qcheckbox.h>
24#endif
25#if QT_CONFIG(radiobutton)
26# include <QtWidgets/qradiobutton.h>
27#endif
28#if QT_CONFIG(combobox)
29# include <QtWidgets/qcombobox.h>
30#endif
31#if QT_CONFIG(slider)
32# include <QtWidgets/qslider.h>
33#endif
34#if QT_CONFIG(scrollbar)
35# include <QtWidgets/qscrollbar.h>
36#endif
37#if QT_CONFIG(spinbox)
38# include <QtWidgets/qspinbox.h>
39#endif
40#if QT_CONFIG(progressbar)
41# include <QtWidgets/qprogressbar.h>
42#endif
43#if QT_CONFIG(textedit)
44# include <QtWidgets/qtextedit.h>
45# include <QtWidgets/qplaintextedit.h>
46#endif
47#if QT_CONFIG(tabbar)
48# include <QtWidgets/qtabbar.h>
49#endif
50#if QT_CONFIG(tabwidget)
51# include <QtWidgets/qtabwidget.h>
52#endif
53#if QT_CONFIG(toolbar)
54# include <QtWidgets/qtoolbar.h>
55#endif
56#if QT_CONFIG(toolbutton)
57# include <QtWidgets/qtoolbutton.h>
58#endif
59#if QT_CONFIG(groupbox)
60# include <QtWidgets/qgroupbox.h>
61#endif
62#if QT_CONFIG(menu)
63# include <QtWidgets/qmenu.h>
64# include <QtWidgets/qmenubar.h>
65#endif
66#if QT_CONFIG(label)
67# include <QtWidgets/qlabel.h>
68#endif
69#include <QtWidgets/private/qwidget_p.h>
70#include <QtCore/private/qobject_p.h>
71#include <QtGui/qpainter.h>
72#include <QtGui/qpainterpath.h>
73#include <QtGui/qpainterstateguard.h>
74#include <QtGui/qstylehints.h>
75#include <QtCore/qloggingcategory.h>
76#include <QtCore/qnumeric.h>
77#include <QtQml/private/qqmlcomponent_p.h>
78#include <QtQml/qqmlengine.h>
79#include <QtQuick/private/qquicktransition_p.h>
80#include <QtLabsStyleKit/private/qqstylekit_p.h>
81#include <QtLabsStyleKit/private/qqstylekitcontrolproperties_p.h>
82#include <QtLabsStyleKit/private/qqstylekitstyle_p.h>
83
85
86Q_STATIC_LOGGING_CATEGORY(lcStyleKit, "qt.labs.stylekit")
87
88/*!
89 \class QStyleKitStyle
90 \inmodule QtLabsStyleKit
91 \ingroup appearance
92 \since 6.12
93
94 \brief The QStyleKitStyle class applies a \l {Qt Labs StyleKit} style
95 to Qt Widgets.
96
97 QStyleKitStyle is a QStyle implementation that uses a \l StyleKit \l Style
98 to style Qt Widgets. The \l Style is a QML file that declaratively describes
99 the visual design (colors, sizes, radii, borders, and other properties)
100 for each widget type and state. Those property values drive the painting,
101 which is done entirely with QPainter. Qt Quick and the scene graph play
102 no part in the rendering.
103
104 This separation means the same \l Style QML file can drive both
105 Qt Quick Controls and Qt Widgets, sharing one design definition across
106 both systems.
107
108 \note StyleKit is a Qt Labs module, and its API may change between
109 Qt releases.
110
111 \section1 Loading a Style
112
113 A style is a QML file whose root object is a \l Style. To load it,
114 pass the file path to the constructor or to \l setStylePath():
115
116 \if !defined(qtforpython)
117 \code
118 auto *style = new QStyleKitStyle(QStringLiteral(":/styles/MyStyle.qml"));
119 QApplication::setStyle(style);
120 \endcode
121 \else
122 \code
123 style = QStyleKitStyle(":/styles/MyStyle.qml")
124 QApplication.setStyle(style)
125 \endcode
126 \endif
127
128 The Style is loaded with an internal QQmlEngine owned by the
129 QStyleKitStyle instance. If the path is invalid or the root object is
130 not a \l Style, a warning is emitted and the style uses a default
131 fallback style until a valid \l stylePath is set.
132
133 \section1 Themes
134
135 A Style may define one or more named \l {Theme}{themes}. The active
136 theme is selected with \l setThemeName(); the list of available
137 themes is exposed through \l availableThemeNames. The special theme name
138 \c System makes the style follow the platform color scheme: when the
139 OS color scheme changes, the active theme is recreated automatically
140 and all widgets are repolished.
141
142 \section1 Widget to StyleKit Control Mapping
143
144 Each Qt Widgets class is mapped to a StyleKit control type, which
145 determines which control entry in the \l Style applies to it. Use the
146 corresponding control entry to configure visual properties for that widget
147 type, including individual parts of the widget such as its background,
148 indicator, handle, etc. See \l ControlStyleProperties for the full list of
149 stylable properties.
150 Properties not set in a specific control entry fall back through the control
151 type hierarchy: for example, \c button falls back to \c abstractButton, which
152 falls back to \c control.
153
154 \table
155 \header
156 \li Qt Widgets class
157 \li StyleKit control
158 \row
159 \li QPushButton (flat)
160 \li \l {AbstractStylableControls::flatButton}{flatButton}
161 \row
162 \li QPushButton
163 \li \l {AbstractStylableControls::button}{button}
164 \row
165 \li QCheckBox
166 \li \l {AbstractStylableControls::checkBox}{checkBox}
167 \row
168 \li QRadioButton
169 \li \l {AbstractStylableControls::radioButton}{radioButton}
170 \row
171 \li QComboBox
172 \li \l {AbstractStylableControls::comboBox}{comboBox}
173 \row
174 \li QSlider
175 \li \l {AbstractStylableControls::slider}{slider}
176 \row
177 \li QScrollBar
178 \li \l {AbstractStylableControls::scrollBar}{scrollBar}
179 \row
180 \li QSpinBox, QDoubleSpinBox
181 \li \l {AbstractStylableControls::spinBox}{spinBox}
182 \row
183 \li QProgressBar
184 \li \l {AbstractStylableControls::progressBar}{progressBar}
185 \row
186 \li QLineEdit
187 \li \l {AbstractStylableControls::textField}{textField}
188 \row
189 \li QTextEdit, QPlainTextEdit
190 \li \l {AbstractStylableControls::textArea}{textArea}
191 \row
192 \li QTabBar
193 \li \l {AbstractStylableControls::tabBar}{tabBar}
194 \row
195 \li QTabWidget
196 \li \l {AbstractStylableControls::page}{page}
197 \row
198 \li QToolBar
199 \li \l {AbstractStylableControls::toolBar}{toolBar}
200 \row
201 \li QToolButton
202 \li \l {AbstractStylableControls::toolButton}{toolButton}
203 \row
204 \li QGroupBox
205 \li \l {AbstractStylableControls::groupBox}{groupBox}
206 \row
207 \li QFrame
208 \li \l {AbstractStylableControls::frame}{frame}
209 \row
210 \li QLabel
211 \li \l {AbstractStylableControls::label}{label}
212 \row
213 \li QMenu
214 \li \l {AbstractStylableControls::menu}{menu}
215 \row
216 \li QMenuBar
217 \li \l {AbstractStylableControls::menuBar}{menuBar}
218 \row
219 \li Everything else
220 \li \l {AbstractStylableControls::control}{control}
221 \endtable
222
223 Widgets not listed above are not yet supported by QStyleKitStyle and will be
224 painted by \l QCommonStyle. Support for remaining widgets is planned for future
225 releases. Conversely, some control entries in \l AbstractStylableControls have no
226 Qt Widgets equivalent and are not applied when styling widgets.
227
228 \section2 Sub-controls within a widget
229
230 Separate sub-controls within a widget can be styled individually, as each one maps
231 to a separate control entry in the \l Style:
232
233 \table
234 \header
235 \li Sub-element
236 \li StyleKit control
237 \row
238 \li \l QStyledItemDelegate items - the default delegate for all Qt item views,
239 including the \l QComboBox popup list
240 \li \l {AbstractStylableControls::itemDelegate}{itemDelegate}
241 \row
242 \li The same items, when user-checkable (i.e. showing a check indicator)
243 \li \l {AbstractStylableControls::checkDelegate}{checkDelegate};
244 falls back to \c itemDelegate for anything not set explicitly
245 \row
246 \li Individual tabs in a \l QTabBar
247 \li \l {AbstractStylableControls::tabButton}{tabButton}
248 \row
249 \li \l QMenu items
250 \li \l {AbstractStylableControls::menuItem}{menuItem}
251 \row
252 \li Separators in a \l QMenu
253 \li \l {AbstractStylableControls::menuSeparator}{menuSeparator}
254 \row
255 \li \l QMenuBar items
256 \li \l {AbstractStylableControls::menuBarItem}{menuBarItem}
257 \row
258 \li Separators in a \l QToolBar
259 \li \l {AbstractStylableControls::toolSeparator}{toolSeparator}
260 \row
261 \li The \l QComboBox popup list container
262 \li \l {AbstractStylableControls::popup}{popup}
263 \endtable
264
265 \section1 Known Limitations
266
267 QStyleKitStyle is in Tech Preview. The following StyleKit features are
268 currently not supported when used with Qt Widgets:
269
270 \list
271 \li \b{Shadows} — shadows are not rendered.
272 \li \b{Delegate scale above 1.0 on a control's background} — a widget cannot paint
273 outside its own rect, so the scaled background is clipped at the widget edge.
274 Use \l {DelegateStyle::}{margins} to inset the background and reserve room for
275 it to grow. Scaling indicators, handles and foregrounds is unaffected.
276 \li \b{Variations} — setting a \l StyleVariation on a widget instance is
277 not yet supported.
278 \li \b{Custom controls} — styling custom widgets using \l CustomControl
279 is not yet supported.
280 \li \b{Custom delegates} — the \l {DelegateStyle::}{delegate} property is
281 not used; the built-in rendering is always applied.
282 \endlist
283
284 Support for these features is planned for a future release.
285
286 \sa QStyle, QCommonStyle, {Qt Labs StyleKit}, Style, Theme
287*/
288
289/*!
290 \property QStyleKitStyle::stylePath
291 \brief the path to the QML \l Style file driving this style.
292
293 The value is a path to a local file or a path to a file in the resource
294 file system (for example, \c{:/styles/MyStyle.qml}). A relative path is
295 resolved against the application's working directory. The file must
296 contain a QML component whose root object is a \l Style. Setting this
297 property reloads the style; if the new file cannot be loaded, the
298 previously loaded style is kept and a warning is emitted.
299*/
300
301/*!
302 \property QStyleKitStyle::themeName
303 \brief the name of the active theme.
304
305 The value must be one of the entries in \l availableThemeNames, or the
306 special name \c System to follow the platform color scheme.
307 Setting this property updates all widgets to repaint with the
308 new theme.
309*/
310
311/*!
312 \property QStyleKitStyle::availableThemeNames
313 \brief the list of theme names exposed by the loaded \l Style.
314
315 This list includes the built-in \c Light and \c Dark themes as well
316 as any custom themes defined by the style.
317*/
318
319/*!
320 \property QStyleKitStyle::customThemeNames
321 \brief the list of custom theme names defined by the loaded \l Style.
322
323 Unlike \l availableThemeNames, this list excludes the built-in \c Light and
324 \c Dark themes and contains only the themes explicitly defined by the
325 style author. Returns an empty list when no style is loaded.
326
327 \sa availableThemeNames, themeName
328*/
329
330static QQStyleKitReader::ControlType controlTypeForWidget(const QWidget *widget)
331{
332 if (!widget)
333 return QQStyleKitReader::Control;
334
335#if QT_CONFIG(pushbutton)
336 if (qobject_cast<const QPushButton *>(widget))
337 return QQStyleKitReader::Button;
338#endif
339#if QT_CONFIG(checkbox)
340 if (qobject_cast<const QCheckBox *>(widget))
341 return QQStyleKitReader::CheckBox;
342#endif
343#if QT_CONFIG(radiobutton)
344 if (qobject_cast<const QRadioButton *>(widget))
345 return QQStyleKitReader::RadioButton;
346#endif
347#if QT_CONFIG(combobox)
348 if (qobject_cast<const QComboBox *>(widget))
349 return QQStyleKitReader::ComboBox;
350#endif
351#if QT_CONFIG(slider)
352 if (qobject_cast<const QSlider *>(widget))
353 return QQStyleKitReader::Slider;
354#endif
355#if QT_CONFIG(scrollbar)
356 if (qobject_cast<const QScrollBar *>(widget))
357 return QQStyleKitReader::ScrollBar;
358#endif
359#if QT_CONFIG(spinbox)
360 if (qobject_cast<const QSpinBox *>(widget) || qobject_cast<const QDoubleSpinBox *>(widget))
361 return QQStyleKitReader::SpinBox;
362#endif
363#if QT_CONFIG(itemviews)
364 if (qobject_cast<const QAbstractItemView *>(widget))
365 return QQStyleKitReader::ItemDelegate;
366#endif
367#if QT_CONFIG(progressbar)
368 if (qobject_cast<const QProgressBar *>(widget))
369 return QQStyleKitReader::ProgressBar;
370#endif
371#if QT_CONFIG(lineedit)
372 if (qobject_cast<const QLineEdit *>(widget))
373 return QQStyleKitReader::TextField;
374#endif
375#if QT_CONFIG(textedit)
376 if (qobject_cast<const QTextEdit *>(widget) || qobject_cast<const QPlainTextEdit *>(widget))
377 return QQStyleKitReader::TextArea;
378#endif
379#if QT_CONFIG(tabbar)
380 if (qobject_cast<const QTabBar *>(widget))
381 return QQStyleKitReader::TabBar;
382#endif
383#if QT_CONFIG(tabwidget)
384 if (qobject_cast<const QTabWidget *>(widget))
385 return QQStyleKitReader::Page;
386#endif
387#if QT_CONFIG(toolbar)
388 if (qobject_cast<const QToolBar *>(widget))
389 return QQStyleKitReader::ToolBar;
390#endif
391#if QT_CONFIG(toolbutton)
392 if (qobject_cast<const QToolButton *>(widget))
393 return QQStyleKitReader::ToolButton;
394#endif
395#if QT_CONFIG(groupbox)
396 if (qobject_cast<const QGroupBox *>(widget))
397 return QQStyleKitReader::GroupBox;
398#endif
399#if QT_CONFIG(menu)
400 if (qobject_cast<const QMenuBar *>(widget))
401 return QQStyleKitReader::MenuBar;
402 if (qobject_cast<const QMenu *>(widget))
403 return QQStyleKitReader::Menu;
404#endif
405 if (widget->windowType() & Qt::Popup)
406 return QQStyleKitReader::Popup;
407 if (widget->windowType() & Qt::Dialog)
408 return QQStyleKitReader::Dialog;
409 if (widget->windowType() & Qt::Window)
410 return QQStyleKitReader::ApplicationWindow;
411#ifndef QT_NO_FRAME
412 if (qobject_cast<const QFrame *>(widget))
413 return QQStyleKitReader::Frame;
414#endif
415
416 return QQStyleKitReader::Control;
417}
418
419#if QT_CONFIG(itemviews)
420static QQStyleKitReader::ControlType itemViewControlType(const QStyleOption *opt)
421{
422 const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt);
423 return itemViewOption && (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator)
424 ? QQStyleKitReader::ControlType::CheckDelegate
425 : QQStyleKitReader::ControlType::ItemDelegate;
426}
427#endif
428
429// Some widgets (like QAbstractScrollArea) paint on a child widget (the viewport)
430// instead of on themselves. This function returns the widget that is actually painted on,
431// which is the one that should be updated when the style values change.
432static QWidget *paintTarget(const QWidget *widget)
433{
434#if QT_CONFIG(scrollarea)
435 if (const auto *area = qobject_cast<const QAbstractScrollArea *>(widget))
436 return area->viewport();
437#endif
438 return const_cast<QWidget *>(widget);
439}
440
441// Returns true for widgets that draw themselves using their widget font and
442// palette directly, bypassing the style's drawControl path.
443static bool isSelfPaintingWidget(const QWidget *widget)
444{
445 return false
446#if QT_CONFIG(label)
447 || qobject_cast<const QLabel *>(widget)
448#endif
449#if QT_CONFIG(textedit)
450 || qobject_cast<const QTextEdit *>(widget)
451 || qobject_cast<const QPlainTextEdit *>(widget)
452#endif
453#if QT_CONFIG(lineedit)
454 || qobject_cast<const QLineEdit *>(widget)
455#endif
456 ;
457}
458
459static QWidget *managedViewport(QWidget *widget)
460{
461#if QT_CONFIG(textedit)
462 if (auto *textEdit = qobject_cast<QTextEdit *>(widget))
463 return textEdit->viewport();
464 if (auto *plainTextEdit = qobject_cast<QPlainTextEdit *>(widget))
465 return plainTextEdit->viewport();
466#endif
467#if QT_CONFIG(itemviews) && QT_CONFIG(combobox)
468 if (auto *view = qobject_cast<QAbstractItemView *>(widget)) {
469 if (auto *p = view->parentWidget(); p && p->inherits("QComboBoxPrivateContainer"))
470 return view->viewport();
471 }
472#endif
473 return nullptr;
474}
475
476static uint resolvedAlignment(uint raw, Qt::Alignment hDefault, Qt::Alignment vDefault)
477{
478 uint result = 0;
479 const uint h = raw & Qt::AlignHorizontal_Mask;
480 const uint v = raw & Qt::AlignVertical_Mask;
481 result |= h ? h : uint(hDefault);
482 result |= v ? v : uint(vDefault);
483 return result;
484}
485
486static qreal resolvedWidth(const QQStyleKitDelegateProperties *element, qreal availableW)
487{
488 return qMax(0.0, element->fillWidth() ? availableW : element->width());
489}
490
491static qreal resolvedHeight(const QQStyleKitDelegateProperties *element, qreal availableH)
492{
493 return qMax(0.0, element->fillHeight() ? availableH : element->height());
494}
495
496static QMargins elementMargins(const QQStyleKitDelegateProperties *element)
497{
498 using QtPrivate::qSaturateRound;
499 return QMargins(qSaturateRound(element->leftMargin()),
500 qSaturateRound(element->topMargin()),
501 qSaturateRound(element->rightMargin()),
502 qSaturateRound(element->bottomMargin()));
503}
504
505/*! \internal
506 Applies \a element's rotation and scale to \a painter about the centre of \a box.
507 Returns \c false if the scale is 0 or if \a element is null, otherwise returns \c true.
508*/
509static bool applyDelegateTransform(QPainter *painter,
510 const QQStyleKitDelegateProperties *element,
511 const QRectF &box)
512{
513 if (!element)
514 return false;
515
516 const qreal scale = element->scale();
517 if (qFuzzyIsNull(scale))
518 return false;
519
520 const qreal rotation = element->rotation();
521 if (qFuzzyIsNull(rotation) && qFuzzyCompare(scale, 1.0))
522 return true;
523
524 const QPointF center = box.center();
525 painter->translate(center);
526 if (!qFuzzyIsNull(rotation))
527 painter->rotate(rotation);
528 if (!qFuzzyCompare(scale, 1.0))
529 painter->scale(scale, scale);
530 painter->translate(-center);
531 return true;
532}
533
534// Copied from qstylesheetstyle.cpp
535static const QWidget *containerWidget(const QWidget *w)
536{
537#if QT_CONFIG(lineedit)
538 if (qobject_cast<const QLineEdit *>(w)) {
539 // if the QLineEdit is an embeddedWidget, we need the real widget
540 QWidget *parent = w->parentWidget();
541 if (false
542# if QT_CONFIG(combobox)
543 || qobject_cast<const QComboBox *>(parent)
544# endif
545# if QT_CONFIG(spinbox)
546 || qobject_cast<const QAbstractSpinBox *>(parent)
547# endif
548 ) {
549 return parent;
550 }
551 }
552#endif
553
554#if QT_CONFIG(scrollarea)
555 if (const QAbstractScrollArea *sa =
556 qobject_cast<const QAbstractScrollArea *>(w->parentWidget())) {
557 if (sa->viewport() == w)
558 return w->parentWidget();
559 }
560#endif
561
562 return w;
563}
564
565QStyleKitStylePrivate::QStyleKitStylePrivate()
566 : QCommonStylePrivate()
567{
568}
569
570static QUrl urlFromStylePath(const QString &filePath)
571{
572 return filePath.startsWith(QLatin1Char(':')) ? QUrl(QLatin1String("qrc") + filePath)
573 : QUrl::fromLocalFile(filePath);
574}
575
576bool QStyleKitStylePrivate::loadStyle()
577{
578 Q_Q(QStyleKitStyle);
579 if (!qmlEngine)
580 qmlEngine = new QQmlEngine(q);
581
582 if (!qmlEngine) {
583 qWarning("QStyleKitStyle: No QML engine available to load style.");
584 return false;
585 }
586 const QUrl url = stylePath.isEmpty() ? QUrl() : urlFromStylePath(stylePath);
587 if (stylePath.isEmpty() || !url.isValid()) {
588 qWarning("QStyleKitStyle: No valid style path provided: %s", qPrintable(stylePath));
589 return false;
590 }
591 QQmlComponent component(qmlEngine, url);
592 if (component.isError()) {
593 qWarning("QStyleKitStyle: Failed to load style from %s: %s", qPrintable(stylePath),
594 qPrintable(component.errorString()));
595 return false;
596 }
597 // Avoid creating anything other than StyleKit Style objects
598 // by checking the metaobject of the root type before creating the object
599 QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(&component);
600 const auto compilationUnit = componentPrivate->compilationUnit();
601 const auto propertyCache = compilationUnit ? compilationUnit->rootPropertyCache() : nullptr;
602 const auto firstMetaObject = propertyCache ? propertyCache->firstCppMetaObject() : nullptr;
603 if (!firstMetaObject || !firstMetaObject->inherits(&QQStyleKitStyle::staticMetaObject)) {
604 qWarning("QStyleKitStyle: Failed to load style from %s: component is not a StyleKit Style.",
605 qPrintable(stylePath));
606 return false;
607 }
608 QQStyleKitStyle *styleObject = qobject_cast<QQStyleKitStyle *>(component.create());
609 if (!styleObject) {
610 qWarning("QStyleKitStyle: Failed to create style object from %s: "
611 "component is not a StyleKit style.",
612 qPrintable(stylePath));
613 return false;
614 }
615 const bool isReload = style != nullptr;
616 delete style;
617 style = styleObject;
618 style->setParent(q);
619
620 if (!isReload) {
621 QObject::connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, q,
622 [this]() {
623 if (style && style->themeName() == QLatin1String("System")) {
624 style->recreateTheme();
625 updateStyle();
626 }
627 });
628 }
629
630 if (style->loaded())
631 QQStyleKitReader::resetReadersForStyle(style);
632
633 return true;
634}
635
636/*! \internal
637 Creates and returns the empty fallback style used when no user
638 style is loaded.
639*/
640QQStyleKitStyle *QStyleKitStylePrivate::ensureDefaultStyle()
641{
642 if (defaultStyle)
643 return defaultStyle;
644
645 Q_Q(QStyleKitStyle);
646 if (!qmlEngine)
647 qmlEngine = new QQmlEngine(q);
648
649 QQmlComponent component(qmlEngine);
650 component.loadFromModule("Qt.labs.StyleKit", "Style");
651 if (component.isError()) {
652 qWarning("QStyleKitStyle: Failed to create default fallback style: %s",
653 qPrintable(component.errorString()));
654 return nullptr;
655 }
656 defaultStyle = qobject_cast<QQStyleKitStyle *>(component.create());
657 if (defaultStyle) {
658 defaultStyle->setParent(q);
659 qCDebug(lcStyleKit,
660 "No style set; using a default fallback style. "
661 "Set stylePath to load a StyleKit Style.");
662 }
663 return defaultStyle;
664}
665
666/*! \internal
667 Returns the user style if one is loaded, otherwise the default fallback
668 style
669*/
670QQStyleKitStyle *QStyleKitStylePrivate::effectiveStyle() const
671{
672 return style ? style : defaultStyle;
673}
674
675void QStyleKitStylePrivate::updateStyle()
676{
677 clearMetricsCache();
678
679 QQStyleKitStyle *effective = effectiveStyle();
680
681 if (sharedReader && sharedReader->style() != effective)
682 sharedReader->setExplicitStyle(effective);
683
684 if (subElementReader && subElementReader->style() != effective)
685 subElementReader->setExplicitStyle(effective);
686
687 clearAllSubElements();
688
689 for (auto *wr : std::as_const(widgetReaders)) {
690 if (wr->style() != effective)
691 wr->setExplicitStyle(effective);
692 }
693
694 if (!QWidgetPrivate::allWidgets)
695 return;
696
697 for (auto *widget : std::as_const(*QWidgetPrivate::allWidgets)) {
698 if (customFontWidgets.contains(widget))
699 unsetStyleFont(widget);
700 refreshStyleFont(widget);
701 if (customPaletteWidgets.contains(widget))
702 unsetStylePalette(widget);
703 refreshStylePalette(widget);
704 }
705
706 QEvent styleChange(QEvent::StyleChange);
707 for (auto *widget : std::as_const(*QWidgetPrivate::allWidgets)) {
708 QApplication::sendEvent(widget, &styleChange);
709 widget->update();
710 }
711}
712
713// Follow the approach in qstylesheetstyle.cpp
714void QStyleKitStylePrivate::unsetStyleFont(QWidget *widget)
715{
716 auto it = customFontWidgets.find(widget);
717 if (it == customFontWidgets.end())
718 return;
719
720 auto customFont = std::move(*it);
721 customFontWidgets.erase(it);
722 widget->setFont(std::move(customFont).reverted(widget->font()));
723}
724
725void QStyleKitStylePrivate::setStyleFont(QWidget *widget, const QFont &styleFont)
726{
727 if (!effectiveStyle() || !widget)
728 return;
729
730 const auto styleMask = styleFont.resolveMask();
731 auto it = customFontWidgets.find(widget);
732
733 if (it == customFontWidgets.end()) {
734 if (styleMask == 0)
735 return;
736 it = customFontWidgets.insert(widget, { QWidgetPrivate::get(widget)->localFont(), 0 });
737 QObject::connect(widget, &QObject::destroyed, widget,
738 [this, widget]() { customFontWidgets.remove(widget); });
739 }
740
741 it->resolveMask = styleMask;
742
743 const QFont &baseline = it->oldWidgetValue;
744 QFont merged = styleFont.resolve(baseline);
745 merged.setResolveMask(baseline.resolveMask() | styleMask);
746 if (widget->font() != merged)
747 widget->setFont(merged);
748}
749
750void QStyleKitStylePrivate::refreshStyleFont(QWidget *widget)
751{
752 if (!effectiveStyle() || !widget)
753 return;
754
755 const QWidget *targetWidget = containerWidget(widget);
756 QQStyleKitReader::ControlType controlType = controlTypeForWidget(targetWidget);
757
758 auto *shared = ensureSharedReader();
759 if (!shared)
760 return;
761
762 QStyleOption opt;
763 opt.initFrom(targetWidget);
764 const QQSK::State currentState = resolvedStateFor(controlType, opt.state, targetWidget);
765
766 shared->setControlTypeAndState(controlType, currentState);
767 setStyleFont(widget, shared->font());
768}
769
770void QStyleKitStylePrivate::unsetStylePalette(QWidget *widget)
771{
772 auto it = customPaletteWidgets.find(widget);
773 if (it == customPaletteWidgets.end())
774 return;
775
776 auto customPalette = std::move(*it);
777 customPaletteWidgets.erase(it);
778 widget->setPalette(std::move(customPalette).reverted(widget->palette()));
779}
780
781void QStyleKitStylePrivate::setStylePalette(QWidget *widget, const QPalette &stylePalette) const
782{
783 if (!effectiveStyle() || !widget)
784 return;
785
786 const quint64 styleMask = stylePalette.resolveMask();
787 if (styleMask == 0)
788 return;
789
790 if (!customPaletteWidgets.contains(widget)) {
791 customPaletteWidgets.insert(widget, { widget->palette(), 0 });
792 QObject::connect(widget, &QObject::destroyed, widget,
793 [this, widget]() { customPaletteWidgets.remove(widget); });
794 }
795
796 customPaletteWidgets[widget].resolveMask |= styleMask;
797 QPalette merged = stylePalette.resolve(widget->palette());
798 merged.setResolveMask(widget->palette().resolveMask() | styleMask);
799 widget->setPalette(merged);
800}
801
802// For widgets that partially or fully draw themselves (instead of delegating to the style),
803// using palette roles, we need to push the style colors to those roles
804void QStyleKitStylePrivate::refreshStylePalette(QWidget *widget)
805{
806 if (!effectiveStyle() || !widget)
807 return;
808
809 const bool isWindow = widget->windowType() & (Qt::Popup | Qt::Window | Qt::Dialog);
810 const bool isPaletteManaged = isSelfPaintingWidget(widget) || isWindow;
811 if (!isPaletteManaged)
812 return;
813
814 const QWidget *targetWidget = containerWidget(widget);
815 QQStyleKitReader::ControlType controlType = controlTypeForWidget(targetWidget);
816
817 auto *shared = ensureSharedReader();
818 if (!shared)
819 return;
820
821 QStyleOption opt;
822 opt.initFrom(targetWidget);
823 const QQSK::State currentState = resolvedStateFor(controlType, opt.state, targetWidget);
824
825 QPalette stylePalette;
826 if (isWindow) {
827 // Windows draw their own background using the QPalette::Window role
828 shared->setControlTypeAndState(controlType, currentState);
829 if (const auto *bg = shared->global()->background();
830 bg && bg->isDefined(QQSK::Property::Color)) {
831 stylePalette.setColor(QPalette::Window, bg->color());
832 }
833 shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled);
834 if (const auto *dbg = shared->global()->background();
835 dbg && dbg->isDefined(QQSK::Property::Color)) {
836 stylePalette.setColor(QPalette::Disabled, QPalette::Window, dbg->color());
837 }
838 } else {
839 // The remaining text-based widgets use the QPalette::Text/WindowText roles for their
840 // foreground color
841 shared->setControlTypeAndState(controlType, currentState);
842 if (const auto *text = shared->global()->text();
843 text && text->isDefined(QQSK::Property::Color)) {
844 stylePalette.setColor(QPalette::Text, text->color());
845 stylePalette.setColor(QPalette::WindowText, text->color());
846 }
847 shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled);
848 if (const auto *dt = shared->global()->text();
849 dt && dt->isDefined(QQSK::Property::Color)) {
850 stylePalette.setColor(QPalette::Disabled, QPalette::Text, dt->color());
851 stylePalette.setColor(QPalette::Disabled, QPalette::WindowText, dt->color());
852 }
853 }
854
855 setStylePalette(widget, stylePalette);
856}
857
858/*! \internal
859 Returns a reader for the given widget, creating and caching it if needed.
860*/
861QQStyleKitReader *QStyleKitStylePrivate::readerForWidget(const QWidget *widget) const
862{
863 Q_Q(const QStyleKitStyle);
864 QQStyleKitStyle *effective = effectiveStyle();
865 if (!effective || !widget)
866 return nullptr;
867
868 if (auto it = widgetReaders.find(widget); it != widgetReaders.end())
869 return *it;
870
871 auto *widgetReader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
872 widgetReader->setExplicitStyle(effective);
873 widgetReader->setTarget(paintTarget(widget));
874 widgetReader->setControlType(controlTypeForWidget(widget));
875 widgetReader->setCompleted(true);
876 widgetReaders.insert(widget, widgetReader);
877 QObjectPrivate::connect(widget, &QObject::destroyed, this,
878 &QStyleKitStylePrivate::onWidgetDestroyed, Qt::UniqueConnection);
879 return widgetReader;
880}
881
882void QStyleKitStylePrivate::cleanupWidgetReader(const QWidget *widget) const
883{
884 if (auto *reader = widgetReaders.take(widget))
885 reader->deleteLater();
886 cleanupSubElements(widget);
887}
888
889void QStyleKitStylePrivate::onWidgetDestroyed(QObject *w) const
890{
891 cleanupWidgetReader(static_cast<QWidget *>(w));
892}
893
894/*! \internal
895 Returns the key identifying the sub-element (item-view cell, menu item or
896 menubar item) that \a opt refers to, or an invalid key if the option does
897 not identify one.
898*/
899QStyleKitStylePrivate::SubElementKey
900QStyleKitStylePrivate::subElementKeyForOption(const QStyleOption *opt, const QWidget *widget)
901{
902 SubElementKey key;
903 if (!widget)
904 return key;
905
906#if QT_CONFIG(itemviews)
907 if (const auto *viewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
908 const QModelIndex &idx = viewOpt->index;
909 if (!idx.isValid())
910 return key;
911 key.widget = widget;
912 key.id = SubElementKey::ItemViewCell{ idx.model(), idx.internalId(), idx.row(),
913 idx.column() };
914 return key;
915 }
916#endif
917#if QT_CONFIG(menu)
918 if (const auto *menuOpt = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
919 if (menuOpt->menuItemType == QStyleOptionMenuItem::Separator)
920 return key;
921 QAction *action = nullptr;
922 if (const auto *menu = qobject_cast<const QMenu *>(widget))
923 action = menu->actionAt(menuOpt->rect.center());
924 else if (const auto *menuBar = qobject_cast<const QMenuBar *>(widget))
925 action = menuBar->actionAt(menuOpt->rect.center());
926 if (action) {
927 key.widget = widget;
928 key.id = SubElementKey::Action{ action };
929 }
930 return key;
931 }
932#endif
933#if QT_CONFIG(tabbar)
934 if (const auto *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
935 if (tabOpt->tabIndex < 0)
936 return key;
937 key.widget = widget;
938 key.id = SubElementKey::Tab{ tabOpt->tabIndex };
939 return key;
940 }
941#endif
942 return key;
943}
944
945/*! \internal
946 Creates and returns a transitions-enabled reader for the given sub-element
947 that animates from \a fromState to \a toState
948
949*/
950QQStyleKitReader *QStyleKitStylePrivate::startSubElementTransition(
951 const SubElementKey &key, QQStyleKitReader::ControlType type, QQSK::State fromState,
952 QQSK::State toState, QQuickTransition *transition) const
953{
954 Q_Q(const QStyleKitStyle);
955 QQStyleKitStyle *effective = effectiveStyle();
956 if (!effective)
957 return nullptr;
958
959 // Reclaim finished readers. running() is per (shared) transition object, so
960 // it turns false only once no instance of that transition animates anywhere
961 for (auto it = subElementAnimationReaders.begin(); it != subElementAnimationReaders.end();) {
962 const QQuickTransition *t = it.value()->transition();
963 if (!t || !t->running()) {
964 it.value()->deleteLater();
965 it = subElementAnimationReaders.erase(it);
966 } else {
967 ++it;
968 }
969 }
970
971
972 if (subElementAnimationReaders.size() >= kMaxSubElementAnimationReaders) {
973 const auto it = subElementAnimationReaders.begin();
974 if (const QWidget *widget = it.key().widget)
975 paintTarget(widget)->update(); // snap to end state
976 it.value()->deleteLater();
977 subElementAnimationReaders.erase(it);
978 }
979
980 auto *reader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
981 reader->setExplicitStyle(effective);
982 reader->setTarget(paintTarget(key.widget));
983 // just stores the fromState, doesn't start the animation yet
984 reader->setControlTypeAndState(type, fromState);
985 reader->setCompleted(true);
986 // As delegates are created lazily, a fresh reader has none,
987 // so we need to create them now so delegates can already start animating changes
988 // Only create delegates actually used by subelements, ie: background, indicators
989 reader->background();
990 auto *indicator = reader->indicator();
991 indicator->first()->foreground();
992 indicator->second()->foreground();
993 // Start the transition to the new state
994 reader->setControlTypeAndState(type, toState);
995 if (!transition || !transition->running()) {
996 reader->deleteLater();
997 return nullptr;
998 }
999 subElementAnimationReaders.insert(key, reader);
1000 return reader;
1001}
1002
1003/*! \internal
1004 Drops all sub-element state tracking and animation readers for \a widget.
1005*/
1006void QStyleKitStylePrivate::cleanupSubElements(const QWidget *widget) const
1007{
1008 for (auto it = subElementStates.begin(); it != subElementStates.end();) {
1009 if (it.key().widget == widget)
1010 it = subElementStates.erase(it);
1011 else
1012 ++it;
1013 }
1014 for (auto it = subElementAnimationReaders.begin(); it != subElementAnimationReaders.end();) {
1015 if (it.key().widget == widget) {
1016 it.value()->deleteLater();
1017 it = subElementAnimationReaders.erase(it);
1018 } else {
1019 ++it;
1020 }
1021 }
1022}
1023
1024/*! \internal
1025 Drops all sub-element state tracking and animation readers.
1026*/
1027void QStyleKitStylePrivate::clearAllSubElements() const
1028{
1029 subElementStates.clear();
1030 for (QQStyleKitReader *reader : std::as_const(subElementAnimationReaders))
1031 reader->deleteLater();
1032 subElementAnimationReaders.clear();
1033}
1034
1035/*! \internal
1036 Resolves the given QStyle::State into a QQSK::State based on the mapping of
1037 state flags for the given control type.
1038*/
1039QQSK::State QStyleKitStylePrivate::resolvedStateFor(QQStyleKitReader::ControlType type,
1040 QStyle::State state,
1041 const QWidget *widget) const
1042{
1043 QQSK::State flags;
1044 flags.setFlag(QQSK::StateFlag::Hovered, (state & QStyle::State_MouseOver));
1045 flags.setFlag(QQSK::StateFlag::Pressed, state & QStyle::State_Sunken);
1046 flags.setFlag(QQSK::StateFlag::Checked, state & QStyle::State_On);
1047 flags.setFlag(QQSK::StateFlag::Focused, state & QStyle::State_HasFocus);
1048 flags.setFlag(QQSK::StateFlag::Highlighted, state & QStyle::State_Selected);
1049
1050 bool hasOrientation = false;
1051 switch (type) {
1052 case QQStyleKitReader::ProgressBar:
1053 case QQStyleKitReader::ScrollBar:
1054 case QQStyleKitReader::ScrollIndicator:
1055 case QQStyleKitReader::Slider:
1056 case QQStyleKitReader::ToolBar:
1057 case QQStyleKitReader::ToolSeparator:
1058 hasOrientation = true;
1059 break;
1060 default:
1061 break;
1062 }
1063 if (hasOrientation)
1064 flags.setFlag(QQSK::StateFlag::Vertical, !(state & QStyle::State_Horizontal));
1065 // Some widgets don't set State_Enabled in their
1066 // style option even when the widget is enabled.
1067 // Fall back to widget->isEnabled() in that case
1068 const bool enabled = (state & QStyle::State_Enabled) || (widget && widget->isEnabled());
1069 flags.setFlag(QQSK::StateFlag::Disabled, !enabled);
1070
1071 // ComboBox uses QStyle::State_On to indicate the popup is open, which is
1072 // not a "checked" semantic.
1073 if (type == QQStyleKitReader::ControlType::ComboBox)
1074 flags.setFlag(QQSK::StateFlag::Checked, false);
1075
1076 // Popup has no hover state in the Controls style
1077 if (type == QQStyleKitReader::ControlType::Popup)
1078 flags.setFlag(QQSK::StateFlag::Hovered, false);
1079
1080 // QTabBar sets State_Selected (not State_On) on the current tab
1081 if (type == QQStyleKitReader::ControlType::TabButton) {
1082 flags.setFlag(QQSK::StateFlag::Checked, state & QStyle::State_Selected);
1083 flags.setFlag(QQSK::StateFlag::Highlighted, false);
1084 }
1085
1086 return flags;
1087}
1088
1089/*! \internal
1090 Returns the ControlMetrics for the given control type and state,
1091 reading from the style if needed and caching the result.
1092*/
1093const QStyleKitStylePrivate::ControlMetrics &
1094QStyleKitStylePrivate::metricsFor(QQStyleKitReader::ControlType type, QQSK::State state) const
1095{
1096 if (auto it = metricsCache.find({ type, state }); it != metricsCache.end())
1097 return *it;
1098
1099 auto *reader = ensureSharedReader();
1100 Q_ASSERT(reader);
1101 reader->setControlTypeAndState(type, state);
1102 return *metricsCache.insert({ type, state }, metricsForReader(reader));
1103}
1104
1105/*! \internal
1106 Resolves the properties for the given widget, control type and state,
1107 and returns them as a QQStyleKitResolved.
1108*/
1109QStyleKitStylePrivate::QQStyleKitResolved
1110QStyleKitStylePrivate::resolve(const QWidget *w, QQStyleKitReader::ControlType type,
1111 QStyle::State state) const
1112{
1113 QQStyleKitResolved out;
1114 out.widget = w;
1115
1116 auto *reader = readerForWidget(w);
1117 if (!reader)
1118 return out;
1119
1120 const QQSK::State resolvedState = resolvedStateFor(type, state, w);
1121 reader->setControlTypeAndState(type, resolvedState);
1122
1123 out.reader = reader;
1124 out.metrics = &metricsFor(type, resolvedState);
1125 return out;
1126}
1127
1128/*! \internal
1129 Resolves the properties for a sub-element of \a w (an item-view item, menu/
1130 menuBar item), and returns them as a QQStyleKitResolved.
1131
1132 Sub-elements in a steady-state use the shared, transitions-disabled
1133 subElementReader. When a sub-element is observed to change state and
1134 the style defines a transition for the new state, a transient per-item
1135 reader is created to run that transition
1136
1137 Pass \a track = false for synthetic resolves that should not register state changes
1138 or retarget a running animation.
1139*/
1140QStyleKitStylePrivate::QQStyleKitResolved
1141QStyleKitStylePrivate::resolveSubElement(const QWidget *w, const QStyleOption *opt,
1142 QQStyleKitReader::ControlType type, QStyle::State state,
1143 bool track) const
1144{
1145 QQStyleKitResolved out;
1146 out.widget = w;
1147
1148 auto *steadyReader = ensureSubElementReader();
1149 if (!steadyReader)
1150 return out;
1151
1152 const QQSK::State newState = resolvedStateFor(type, state, w);
1153 const SubElementKey key = subElementKeyForOption(opt, w);
1154
1155 QQStyleKitReader *reader = nullptr;
1156 if (track && key.isValid()) {
1157 QQStyleKitReader *animReader = subElementAnimationReaders.value(key);
1158 const auto it = subElementStates.constFind(key);
1159 const bool seen = it != subElementStates.cend();
1160
1161 // Fallback to a reasonable base state to transition from by stripping the
1162 // interactive/pointer-driven flags from the new state.
1163 constexpr QQSK::State InteractiveStates = QQSK::State(QQSK::StateFlag::Hovered)
1164 | QQSK::StateFlag::Pressed | QQSK::StateFlag::Checked
1165 | QQSK::StateFlag::Focused | QQSK::StateFlag::Highlighted;
1166 const auto baseState = newState & ~InteractiveStates;
1167 const QQSK::State prevState = seen ? *it : baseState;
1168 if (prevState != newState) {
1169 if (animReader) {
1170 // State changed before transition finished
1171 // Retarget to new state, the transition continues from the current interpolated
1172 // values
1173 animReader->setControlTypeAndState(type, newState);
1174 } else {
1175 // Only pointer-driven states should animate
1176 // If an item first comes into view checked/highlighted, etc.,
1177 // it should not animate
1178 const bool shouldAnimate = seen || newState.testFlag(QQSK::StateFlag::Hovered)
1179 || newState.testFlag(QQSK::StateFlag::Pressed);
1180 if (shouldAnimate) {
1181 steadyReader->setControlTypeAndState(type, newState);
1182 if (QQuickTransition *transition = steadyReader->transition())
1183 animReader = startSubElementTransition(key, type, prevState,
1184 newState, transition);
1185 }
1186 }
1187 }
1188 if (!seen || prevState != newState) {
1189 QObjectPrivate::connect(w, &QObject::destroyed, this,
1190 &QStyleKitStylePrivate::onWidgetDestroyed,
1191 Qt::UniqueConnection);
1192 if (subElementStates.size() >= kMaxSubElementStates)
1193 subElementStates.clear();
1194 subElementStates.insert(key, newState);
1195 }
1196 reader = animReader;
1197 }
1198
1199 if (!reader)
1200 reader = steadyReader;
1201 reader->setControlTypeAndState(type, newState);
1202
1203 out.reader = reader;
1204 out.metrics = &metricsFor(type, newState);
1205 return out;
1206}
1207
1208const QQStyleKitTextProperties *QStyleKitStylePrivate::QQStyleKitResolved::text() const
1209{
1210 return reader ? reader->text() : nullptr;
1211}
1212
1213const QQStyleKitDelegateProperties *QStyleKitStylePrivate::QQStyleKitResolved::background() const
1214{
1215 return reader ? reader->background() : nullptr;
1216}
1217
1218const QQStyleKitHandleProperties *QStyleKitStylePrivate::QQStyleKitResolved::handle() const
1219{
1220 return reader ? reader->handle() : nullptr;
1221}
1222
1223const QQStyleKitIndicatorWithSubTypes *QStyleKitStylePrivate::QQStyleKitResolved::indicator() const
1224{
1225 return reader ? reader->indicator() : nullptr;
1226}
1227
1228QFont QStyleKitStylePrivate::QQStyleKitResolved::font() const
1229{
1230 return reader ? reader->font() : QFont();
1231}
1232
1233QQStyleKitReader *QStyleKitStylePrivate::ensureSharedReader() const
1234{
1235 if (sharedReader)
1236 return sharedReader;
1237
1238 Q_Q(const QStyleKitStyle);
1239 QQStyleKitStyle *effective = effectiveStyle();
1240 if (!effective)
1241 return nullptr;
1242 sharedReader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
1243 // Disable transitions since this reader is used for one-off metric reads in layout queries
1244 sharedReader->setTransitionsEnabled(false);
1245 sharedReader->setExplicitStyle(effective);
1246 sharedReader->setCompleted(true);
1247 return sharedReader;
1248}
1249
1250QQStyleKitReader *QStyleKitStylePrivate::ensureSubElementReader() const
1251{
1252 if (subElementReader)
1253 return subElementReader;
1254
1255 Q_Q(const QStyleKitStyle);
1256 QQStyleKitStyle *effective = effectiveStyle();
1257 if (!effective)
1258 return nullptr;
1259 subElementReader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
1260 // Disable transitions: this reader is retargeted across many sub-elements
1261 // per paint pass and must never animate between their states
1262 subElementReader->setTransitionsEnabled(false);
1263 subElementReader->setExplicitStyle(effective);
1264 subElementReader->setCompleted(true);
1265 return subElementReader;
1266}
1267
1268/*! \internal
1269 Resolves the properties for the given control type and state using the shared reader,
1270 which disables transitions and is used for layout queries, so that layout never depends
1271 on animation state.
1272*/
1273QStyleKitStylePrivate::QQStyleKitLayoutResolved
1274QStyleKitStylePrivate::resolveLayout(QQStyleKitReader::ControlType type, QStyle::State state) const
1275{
1276 QQStyleKitLayoutResolved out;
1277 auto *reader = ensureSharedReader();
1278 if (!reader)
1279 return out;
1280
1281 const QQSK::State resolvedState = resolvedStateFor(type, state);
1282 out.metrics = &metricsFor(type, resolvedState);
1283 // metricsFor() sets the sharedReader to (type, resolvedState) on cache miss.
1284 // On hit it didn't, so make sure staticProps / staticFont reflect the state
1285 // and type of the caller
1286 reader->setControlTypeAndState(type, resolvedState);
1287 out.staticProps = reader->global();
1288 out.staticFont = reader->font();
1289 return out;
1290}
1291
1292const QQStyleKitTextProperties *QStyleKitStylePrivate::QQStyleKitLayoutResolved::text() const
1293{
1294 return staticProps ? staticProps->text() : nullptr;
1295}
1296
1297const QQStyleKitDelegateProperties *
1298QStyleKitStylePrivate::QQStyleKitLayoutResolved::background() const
1299{
1300 return staticProps ? staticProps->background() : nullptr;
1301}
1302
1303const QQStyleKitHandleProperties *QStyleKitStylePrivate::QQStyleKitLayoutResolved::handle() const
1304{
1305 return staticProps ? staticProps->handle() : nullptr;
1306}
1307
1309QStyleKitStylePrivate::QQStyleKitLayoutResolved::indicator() const
1310{
1311 return staticProps ? staticProps->indicator() : nullptr;
1312}
1313
1314void QStyleKitStylePrivate::clearMetricsCache()
1315{
1316 metricsCache.clear();
1317}
1318
1319void QStyleKitStylePrivate::drawControlIndicator(const QQStyleKitDelegateProperties *indicator,
1320 const QRectF &rect, QPainter *painter) const
1321{
1322 if (!indicator || !indicator->visible() || indicator->opacity() <= 0 || !rect.isValid())
1323 return;
1324
1325 // The foreground is a "child" of the indicator container so it
1326 // inherits the indicator's transform. Apply it here and let the foreground stack its
1327 // own on top.
1328 QPainterStateGuard stateGuard(painter);
1329 if (!applyDelegateTransform(painter, indicator, rect))
1330 return;
1331
1332 // indicator (background)
1333 QRectF indicatorRect = rect;
1334 drawStyledItemContents(indicator, indicatorRect, painter);
1335
1336 const QQStyleKitDelegateProperties *foreground = nullptr;
1337 if (auto *indicatorWithSubTypes =
1338 qobject_cast<const QQStyleKitIndicatorWithSubTypes *>(indicator)) {
1339 foreground = indicatorWithSubTypes->foreground();
1340 } else if (auto *indicatorProps =
1341 qobject_cast<const QQStyleKitIndicatorProperties *>(indicator)) {
1342 foreground = indicatorProps->foreground();
1343 } else {
1344 return;
1345 }
1346 if (!foreground || !foreground->visible() || foreground->opacity() <= 0)
1347 return;
1348
1349 // foreground
1350 QRectF foregroundRect;
1351 const uint foregroundAlign = resolvedAlignment(foreground->alignment(), Qt::AlignHCenter,
1352 Qt::AlignVCenter);
1353 const auto foregroundW = resolvedWidth(
1354 foreground, indicatorRect.width() - foreground->leftMargin() - foreground->rightMargin());
1355 const auto foregroundH = resolvedHeight(
1356 foreground, indicatorRect.height() - foreground->topMargin() - foreground->bottomMargin());
1357 foregroundRect.setSize(QSizeF(foregroundW, foregroundH));
1358 if (foregroundAlign & Qt::AlignLeft)
1359 foregroundRect.moveLeft(indicatorRect.left() + foreground->leftMargin());
1360 else if (foregroundAlign & Qt::AlignHCenter)
1361 foregroundRect.moveLeft(indicatorRect.left() + (indicatorRect.width() - foregroundW) / 2.0);
1362 else if (foregroundAlign & Qt::AlignRight)
1363 foregroundRect.moveLeft(indicatorRect.right() - foreground->rightMargin() - foregroundW);
1364 if (foregroundAlign & Qt::AlignTop)
1365 foregroundRect.moveTop(indicatorRect.top() + foreground->topMargin());
1366 else if (foregroundAlign & Qt::AlignVCenter)
1367 foregroundRect.moveTop(indicatorRect.top() + (indicatorRect.height() - foregroundH) / 2.0);
1368 else if (foregroundAlign & Qt::AlignBottom)
1369 foregroundRect.moveTop(indicatorRect.bottom() - foreground->bottomMargin() - foregroundH);
1370 drawStyledItemRect(foreground, foregroundRect, painter);
1371}
1372
1373void QStyleKitStylePrivate::drawControlText(const QQStyleKitTextProperties *textProps,
1374 const QFont &font, const QRect &rect,
1375 const QString &text, uint textFlags, QPainter *p,
1376 Qt::Alignment defaultAlignment) const
1377{
1378 uint flags = textFlags;
1379 flags |= textProps
1380 ? resolvedAlignment(textProps->alignment(), defaultAlignment & Qt::AlignHorizontal_Mask,
1381 defaultAlignment & Qt::AlignVertical_Mask)
1382 : uint(defaultAlignment);
1383
1384 const QFont oldFont = p->font();
1385 p->setFont(font.resolve(oldFont));
1386 p->setBrush(Qt::NoBrush);
1387 p->setPen(textProps ? textProps->color() : QColor());
1388 p->drawText(rect, flags, text, nullptr);
1389}
1390
1391void QStyleKitStylePrivate::drawStyledItemRect(const QQStyleKitDelegateProperties *props,
1392 const QRectF &rect, QPainter *painter) const
1393{
1394 if (!props || !props->visible() || props->opacity() <= 0 || !rect.isValid())
1395 return;
1396
1397 QPainterStateGuard stateGuard(painter);
1398 if (!applyDelegateTransform(painter, props, rect))
1399 return;
1400 drawStyledItemContents(props, rect, painter);
1401}
1402
1403void QStyleKitStylePrivate::drawStyledItemContents(const QQStyleKitDelegateProperties *props,
1404 const QRectF &rect, QPainter *painter) const
1405{
1406 if (!props || !props->visible() || props->opacity() <= 0 || !rect.isValid())
1407 return;
1408
1409 QPainterStateGuard stateGuard(painter);
1410 painter->setRenderHint(QPainter::Antialiasing, true);
1411 if (props->clip())
1412 painter->setClipping(true);
1413
1414 // opacity
1415 const auto opacity = props->opacity();
1416
1417 // border
1418 const auto *border = props->border();
1419 auto borderWidth = 0;
1420 auto inset = 0.0;
1421 QColor borderColor;
1422 if (border) {
1423 borderWidth = border->width();
1424 borderColor = border->color();
1425 inset = borderWidth / 2.0;
1426 }
1427 if (borderWidth > 0 && borderColor.isValid() && borderColor.alpha() > 0) {
1428 QPen pen(borderColor, borderWidth);
1429 painter->setPen(pen);
1430 } else {
1431 painter->setPen(Qt::NoPen);
1432 }
1433
1434 // radius
1435 // TODO: support different radius for each corner
1436 const qreal minDimension = qMin(rect.width(), rect.height());
1437 const qreal xRadius = qMax(0.0, qMin(props->topLeftRadius() - inset, minDimension / 2.0));
1438 const qreal yRadius = qMax(0.0, qMin(props->bottomLeftRadius() - inset, minDimension / 2.0));
1439
1440 QRectF adjustedRect = rect.adjusted(inset, inset, -inset, -inset);
1441
1442 // gradients/color
1443 // TODO: support palette roles for colors and gradients
1444 QColor color = props->color();
1445 QBrush colorBrush(color);
1446 painter->setBrush(colorBrush);
1447 if (color.isValid()) {
1448 painter->setOpacity(opacity);
1449 painter->drawRoundedRect(adjustedRect, xRadius, yRadius, Qt::AbsoluteSize);
1450 }
1451 if (QQuickGradient *gradient = props->gradient()) {
1452 QLinearGradient linearGradient(rect.topLeft(), rect.bottomLeft());
1453 QQmlListProperty<QQuickGradientStop> stops = gradient->stops();
1454 const int stopCount = stops.count(&stops);
1455 for (int i = 0; i < stopCount; i++) {
1456 QQuickGradientStop *stop = static_cast<QQuickGradientStop *>(stops.at(&stops, i));
1457 linearGradient.setColorAt(stop->position(), stop->color());
1458 }
1459 QBrush gradientBrush(linearGradient);
1460 painter->setBrush(gradientBrush);
1461 painter->setOpacity(opacity);
1462 painter->drawRoundedRect(adjustedRect, xRadius, yRadius, Qt::AbsoluteSize);
1463 }
1464
1465 // image
1466 drawStyledItemImage(props->image(), adjustedRect, opacity, painter);
1467}
1468
1469void QStyleKitStylePrivate::drawStyledItemImage(const QQStyleKitImageProperties *image,
1470 const QRectF &rect, qreal opacity,
1471 QPainter *painter) const
1472{
1473 if (!image || image->source().isEmpty() || image->color().alpha() <= 0)
1474 return;
1475
1476 QString imageSource = image->source().toString();
1477 if (imageSource.startsWith(QLatin1String("qrc:/")))
1478 imageSource = imageSource.mid(3);
1479 QUrl imageUrl(imageSource);
1480 QString imagePath = imageUrl.isLocalFile() ? imageUrl.toLocalFile() : imageSource;
1481 QPixmap pixmap(imagePath);
1482 if (pixmap.isNull())
1483 return;
1484
1485 if (image->color().isValid()) {
1486 QImage coloredImage = pixmap.toImage();
1487 QPainter imagePainter(&coloredImage);
1488 imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
1489 imagePainter.fillRect(coloredImage.rect(), image->color());
1490 imagePainter.end();
1491 pixmap = QPixmap::fromImage(coloredImage);
1492 }
1493 painter->setOpacity(opacity);
1494 const QSizeF pixmapSize = pixmap.deviceIndependentSize();
1495
1496 switch (image->fillMode()) {
1497 case QQuickImage::PreserveAspectFit: {
1498 const QSize scaled = pixmapSize.scaled(rect.size(), Qt::KeepAspectRatio).toSize();
1499 const QPointF topLeft(rect.x() + (rect.width() - scaled.width()) / 2.0,
1500 rect.y() + (rect.height() - scaled.height()) / 2.0);
1501 painter->drawPixmap(QRectF(topLeft, scaled), pixmap, QRectF(QPointF(0, 0), pixmapSize));
1502 break;
1503 }
1504 case QQuickImage::PreserveAspectCrop: {
1505 const QSize scaled = pixmapSize
1506 .scaled(rect.size(), Qt::KeepAspectRatioByExpanding)
1507 .toSize();
1508 const QPointF topLeft(rect.x() + (rect.width() - scaled.width()) / 2.0,
1509 rect.y() + (rect.height() - scaled.height()) / 2.0);
1510 QPainterStateGuard stateGuard(painter);
1511 painter->setClipRect(rect);
1512 painter->drawPixmap(QRectF(topLeft, scaled), pixmap, QRectF(QPointF(0, 0), pixmapSize));
1513 break;
1514 }
1515 // TODO: Support remaining fill modes
1516 case QQuickImage::Stretch:
1517 default:
1518 painter->drawPixmap(rect, pixmap, QRectF(QPointF(0, 0), pixmapSize));
1519 break;
1520 }
1521}
1522
1523QRect QStyleKitStylePrivate::getAlignedRectInContainer(const QRect &container, const QSize &size,
1524 uint alignment, const QMargins &padding,
1525 const QMargins &margins) const
1526{
1527 QRect r(QPoint(0, 0), size);
1528
1529 if (alignment & Qt::AlignLeft)
1530 r.moveLeft(container.x() + padding.left() + margins.left());
1531 else if (alignment & Qt::AlignHCenter)
1532 r.moveLeft(container.x() + (container.width() - size.width()) / 2);
1533 else // AlignRight
1534 r.moveLeft(container.right() + 1 - padding.right() - margins.right() - size.width());
1535
1536 if (alignment & Qt::AlignTop)
1537 r.moveTop(container.y() + padding.top() + margins.top());
1538 else if (alignment & Qt::AlignVCenter)
1539 r.moveTop(container.y() + (container.height() - size.height()) / 2);
1540 else // AlignBottom
1541 r.moveTop(container.bottom() + 1 - padding.bottom() - margins.bottom() - size.height());
1542
1543 return r;
1544}
1545
1546QStyleKitStylePrivate::ControlMetrics
1547QStyleKitStylePrivate::metricsForReader(QQStyleKitReader *reader) const
1548{
1549 Q_ASSERT(reader);
1550 using QtPrivate::qSaturateRound;
1551 const QQStyleKitControlProperties *props = reader->global();
1552 ControlMetrics metrics;
1553 metrics.bgImplicitSize = QSize(0, 0);
1554 metrics.textPadding = QMargins(0, 0, 0, 0);
1555 metrics.padding = QMargins(qSaturateRound(props->leftPadding()),
1556 qSaturateRound(props->topPadding()),
1557 qSaturateRound(props->rightPadding()),
1558 qSaturateRound(props->bottomPadding()));
1559 metrics.spacing = qSaturateRound(props->spacing());
1560 metrics.margins = QMargins(0, 0, 0, 0);
1561 metrics.indicatorImplicitSize = QSize(0, 0);
1562 metrics.indicatorMargins = QMargins(0, 0, 0, 0);
1563 metrics.foregroundImplicitSize = QSize(0, 0);
1564 metrics.foregroundMargins = QMargins(0, 0, 0, 0);
1565 const auto *background = props->background();
1566
1567 // Note: scale is deliberately absent here as it should not affect layout
1568 const auto elementSize = [](qreal w, qreal h) {
1569 constexpr qreal zero(.0);
1570 return QSizeF(std::max(zero, w), std::max(zero, h)).toSize();
1571 };
1572
1573 if (background) {
1574 metrics.bgImplicitSize = elementSize(background->width(), background->height());
1575 metrics.margins = elementMargins(background);
1576 }
1577 const auto *textProps = props->text();
1578 if (textProps) {
1579 metrics.textPadding = QMargins(qSaturateRound(textProps->leftPadding()),
1580 qSaturateRound(textProps->topPadding()),
1581 qSaturateRound(textProps->rightPadding()),
1582 qSaturateRound(textProps->bottomPadding()));
1583 }
1584 const auto *indicator = props->indicator();
1585 if (indicator) {
1586 metrics.indicatorMargins = elementMargins(indicator);
1587 metrics.indicatorImplicitSize = elementSize(indicator->width(), indicator->height());
1588
1589 const auto *foreground = indicator->foreground();
1590 if (foreground) {
1591 metrics.foregroundMargins = elementMargins(foreground);
1592 const auto foregroundW = resolvedWidth(
1593 foreground,
1594 std::max(.0, qreal(metrics.indicatorImplicitSize.width()
1595 - metrics.foregroundMargins.left()
1596 - metrics.foregroundMargins.right())));
1597 const auto foregroundH = resolvedHeight(
1598 foreground,
1599 std::max(.0, qreal(metrics.indicatorImplicitSize.height()
1600 - metrics.foregroundMargins.top()
1601 - metrics.foregroundMargins.bottom())));
1602 metrics.foregroundImplicitSize = elementSize(foregroundW, foregroundH);
1603 }
1604 }
1605 const auto *handle = props->handle();
1606 if (handle) {
1607 metrics.handleImplicitSize = elementSize(handle->width(), handle->height());
1608 metrics.handleMargins = elementMargins(handle);
1609 }
1610 return metrics;
1611}
1612
1613/*!
1614 Constructs a QStyleKitStyle with no style loaded.
1615
1616 Use \l setStylePath() to load a QML \l Style after construction.
1617 Until a style is loaded, the style uses a default fallback style.
1618*/
1619QStyleKitStyle::QStyleKitStyle()
1620 : QCommonStyle(*new QStyleKitStylePrivate())
1621{
1622}
1623
1624/*!
1625 Constructs a QStyleKitStyle and loads the QML \l Style at \a filePath.
1626
1627 \a filePath is a path to a local file or a path to a file in the resource
1628 file system; a relative path is resolved against the application's working
1629 directory. If the path is invalid or the root object of the loaded component
1630 is not a \l Style, a warning is emitted and the constructed style uses a
1631 default fallback style until a valid \l stylePath is set.
1632*/
1633QStyleKitStyle::QStyleKitStyle(const QString &filePath)
1634 : QCommonStyle(*new QStyleKitStylePrivate())
1635{
1636 Q_D(QStyleKitStyle);
1637 d->stylePath = filePath;
1638 if (!d->loadStyle()) {
1639 qWarning("QStyleKitStyle: Failed to load style from %s", qPrintable(filePath));
1640 }
1641}
1642
1643/*!
1644 Destroys the QStyleKitStyle.
1645*/
1646QStyleKitStyle::~QStyleKitStyle()
1647{
1648}
1649
1650/*!
1651 Returns the path of the currently loaded \l Style file.
1652
1653 \sa setStylePath()
1654*/
1655QString QStyleKitStyle::stylePath() const
1656{
1657 Q_D(const QStyleKitStyle);
1658 return d->stylePath;
1659}
1660
1661/*!
1662 Loads the QML \l Style at \a filePath and applies it to all widgets.
1663
1664 \a filePath is a path to a local file or a path to a file in the resource
1665 file system; see the \l stylePath property for the accepted forms. If it is
1666 the same as the current \l stylePath, this function does nothing. If the new
1667 style cannot be loaded, the previously loaded style remains active and a
1668 warning is emitted; \l stylePathChanged() is still emitted to reflect
1669 the changed property value.
1670
1671 \sa stylePath()
1672*/
1673void QStyleKitStyle::setStylePath(const QString &filePath)
1674{
1675 Q_D(QStyleKitStyle);
1676 if (d->stylePath == filePath)
1677 return;
1678
1679 const QString oldThemeName = themeName();
1680 const QStringList oldThemeNames = availableThemeNames();
1681 const QStringList oldCustomThemeNames = customThemeNames();
1682
1683 d->stylePath = filePath;
1684 if (d->loadStyle()) {
1685 d->updateStyle();
1686 if (const QStringList names = availableThemeNames(); names != oldThemeNames)
1687 emit availableThemeNamesChanged(names);
1688 if (const QStringList names = customThemeNames(); names != oldCustomThemeNames)
1689 emit customThemeNamesChanged(names);
1690 }
1691 emit stylePathChanged(d->stylePath);
1692}
1693
1694/*!
1695 Returns the name of the currently active theme, or an empty string
1696 if no \l Style has been loaded.
1697
1698 \sa setThemeName(), availableThemeNames()
1699*/
1700QString QStyleKitStyle::themeName() const
1701{
1702 Q_D(const QStyleKitStyle);
1703 return d->style ? d->style->themeName() : QString();
1704}
1705
1706/*!
1707 Activates the theme named \a themeName.
1708
1709 \a themeName must be one of the entries in \l availableThemeNames(), or the
1710 special name \c System to follow the platform color scheme. If no
1711 \l Style has been loaded, this function emits a warning and returns
1712 without changing the active theme.
1713
1714 \sa themeName(), availableThemeNames()
1715*/
1716void QStyleKitStyle::setThemeName(const QString &themeName)
1717{
1718 Q_D(QStyleKitStyle);
1719 if (!d->style) {
1720 qWarning("QStyleKitStyle: No style loaded, cannot set theme name.");
1721 return;
1722 }
1723 if (d->style->themeName() == themeName)
1724 return;
1725 d->style->setThemeName(themeName);
1726 d->updateStyle();
1727 emit themeNameChanged(d->style->themeName());
1728}
1729
1730/*!
1731 Returns the names of all themes exposed by the loaded \l Style,
1732 including the built-in \c Light and \c Dark themes and any custom
1733 themes defined by the style. Returns an empty list when no style
1734 is loaded.
1735
1736 \sa customThemeNames(), themeName()
1737*/
1738QStringList QStyleKitStyle::availableThemeNames() const
1739{
1740 Q_D(const QStyleKitStyle);
1741 return d->style ? d->style->availableThemeNames() : QStringList();
1742}
1743
1744/*!
1745 Returns the names of the custom themes defined by the loaded
1746 \l Style, excluding the built-in \c Light and \c Dark themes.
1747 Returns an empty list when no style is loaded.
1748
1749 \sa availableThemeNames()
1750*/
1751QStringList QStyleKitStyle::customThemeNames() const
1752{
1753 Q_D(const QStyleKitStyle);
1754 return d->style ? d->style->customThemeNames() : QStringList();
1755}
1756
1757/*! \reimp */
1758void QStyleKitStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
1759 const QWidget *w) const
1760{
1761 Q_D(const QStyleKitStyle);
1762
1763 switch (pe) {
1764#if QT_CONFIG(menu)
1765 case PE_PanelMenu: {
1766 const auto controlType = w && w->inherits("QComboBoxPrivateContainer")
1767 ? QQStyleKitReader::ControlType::Popup
1768 : QQStyleKitReader::ControlType::Menu;
1769 const auto r = d->resolve(w, controlType, opt->state);
1770 if (!r.isValid())
1771 break;
1772 d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
1773 return;
1774 }
1775 case PE_FrameMenu:
1776 // already drawn in PE_PanelMenu
1777 return;
1778#endif // QT_CONFIG(menu)
1779#if QT_CONFIG(menubar)
1780 case PE_PanelMenuBar: {
1781 const auto r = d->resolve(w, QQStyleKitReader::ControlType::MenuBar, opt->state);
1782 if (!r.isValid())
1783 break;
1784 d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
1785 return;
1786 }
1787#endif // QT_CONFIG(menubar)
1788 case PE_FrameButtonBevel:
1789 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
1790 const auto controlType = btn->features & QStyleOptionButton::Flat
1791 ? QQStyleKitReader::ControlType::FlatButton
1792 : QQStyleKitReader::ControlType::Button;
1793 const auto r = d->resolve(w, controlType, btn->state);
1794 if (!r.isValid())
1795 break;
1796 d->drawStyledItemRect(r.background(), opt->rect, p);
1797 return;
1798 }
1799 break;
1800 case PE_Frame: {
1801 QQStyleKitReader::ControlType controlType;
1802 const bool isPopup = w
1803 && (false
1804#if QT_CONFIG(combobox)
1805 || w->inherits("QComboBoxPrivateContainer")
1806#endif
1807 );
1808 if (isPopup)
1809 controlType = QQStyleKitReader::ControlType::Popup;
1810#if QT_CONFIG(lineedit)
1811 else if (qobject_cast<const QLineEdit *>(w))
1812 controlType = QQStyleKitReader::ControlType::TextField;
1813#endif
1814 else
1815 controlType = QQStyleKitReader::ControlType::Frame;
1816 const auto r = d->resolve(w, controlType, opt->state);
1817 if (!r.isValid())
1818 break;
1819 d->drawStyledItemRect(r.background(), opt->rect, p);
1820 return;
1821 }
1822#if QT_CONFIG(lineedit)
1823 case PE_PanelLineEdit:
1824 if (const auto *lineEdit = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
1825 // LineEdit sets Sunken flag to indicate Sunken frame,
1826 // but the style uses it to indicate pressed state, so ignore it
1827 QStyleOption lineEditOpt(*lineEdit);
1828 lineEditOpt.state &= ~QStyle::State_Sunken;
1829 const auto r = d->resolve(w, QQStyleKitReader::ControlType::TextField,
1830 lineEditOpt.state);
1831 if (!r.isValid())
1832 break;
1833
1834 // LineEdit draws its own text using the Text role so update that role in the palette
1835 if (auto *le = qobject_cast<const QLineEdit *>(w)) {
1836 if (const auto *txt = r.text(); txt && txt->isDefined(QQSK::Property::Color)) {
1837 QPalette stylePalette;
1838 stylePalette.setColor(QPalette::Text, txt->color());
1839 d->setStylePalette(const_cast<QLineEdit *>(le), stylePalette);
1840 }
1841 const_cast<QStyleKitStylePrivate *>(d)->setStyleFont(const_cast<QLineEdit *>(le),
1842 r.font());
1843 }
1844
1845 const QObject *parent = w ? w->parent() : nullptr;
1846# if QT_CONFIG(spinbox)
1847 const bool isInSpinBox = qobject_cast<const QAbstractSpinBox *>(parent);
1848# else
1849 const bool isInSpinBox = false;
1850# endif
1851# if QT_CONFIG(combobox)
1852 const bool isInComboBox = qobject_cast<const QComboBox *>(parent);
1853# else
1854 const bool isInComboBox = false;
1855# endif
1856 // For spinbox and combobox, the line edit doesn't have its own background in the Controls style
1857 if (isInSpinBox || isInComboBox)
1858 return;
1859
1860 d->drawStyledItemRect(r.background(), opt->rect, p);
1861 return;
1862 }
1863 break;
1864#endif // QT_CONFIG(lineedit)
1865#if QT_CONFIG(itemviews)
1866 case PE_PanelItemViewItem: {
1867 const auto r = d->resolveSubElement(w, opt, itemViewControlType(opt), opt->state);
1868 if (!r.isValid())
1869 break;
1870 d->drawStyledItemRect(r.background(), opt->rect, p);
1871 return;
1872 }
1873#endif // QT_CONFIG(itemviews)
1874 case PE_IndicatorCheckBox:
1875 case PE_IndicatorRadioButton: {
1876 const auto controlType = pe == PE_IndicatorCheckBox
1877 ? QQStyleKitReader::ControlType::CheckBox
1878 : QQStyleKitReader::ControlType::RadioButton;
1879 const auto r = d->resolve(w, controlType, opt->state);
1880 if (!r.isValid())
1881 break;
1882 d->drawControlIndicator(r.indicator(), opt->rect, p);
1883 return;
1884 }
1885 case PE_IndicatorArrowDown: {
1886 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ComboBox, opt->state);
1887 if (!r.isValid())
1888 break;
1889 d->drawControlIndicator(r.indicator(), opt->rect, p);
1890 return;
1891 }
1892 case PE_IndicatorItemViewItemCheck: {
1893 // track = false: Checked state is synthetic
1894 const auto r = d->resolveSubElement(w, opt, itemViewControlType(opt), opt->state, false);
1895 if (!r.isValid())
1896 break;
1897 d->drawControlIndicator(r.indicator(), opt->rect, p);
1898 return;
1899 }
1900#if QT_CONFIG(spinbox)
1901 case PE_IndicatorSpinUp:
1902 case PE_IndicatorSpinDown: {
1903 const auto r = d->resolve(w, QQStyleKitReader::ControlType::SpinBox, opt->state);
1904 if (!r.isValid())
1905 break;
1906 const auto *indicator = r.indicator();
1907 const auto *upDownIndicator = indicator
1908 ? (pe == PE_IndicatorSpinUp ? indicator->first() : indicator->second())
1909 : nullptr;
1910 d->drawControlIndicator(upDownIndicator, opt->rect, p);
1911 return;
1912 }
1913#endif // QT_CONFIG(spinbox)
1914#if QT_CONFIG(groupbox)
1915 case PE_FrameGroupBox: {
1916 const auto r = d->resolve(w, QQStyleKitReader::ControlType::GroupBox, opt->state);
1917 if (!r.isValid())
1918 break;
1919 d->drawStyledItemRect(r.background(), opt->rect, p);
1920 return;
1921 }
1922#endif // QT_CONFIG(groupbox)
1923#if QT_CONFIG(tabwidget)
1924 case PE_FrameTabWidget: {
1925 const auto r = d->resolve(w, QQStyleKitReader::ControlType::Page, opt->state);
1926 if (!r.isValid())
1927 break;
1928 d->drawStyledItemRect(r.background(), opt->rect, p);
1929 return;
1930 }
1931#endif // QT_CONFIG(tabwidget)
1932#if QT_CONFIG(tabbar)
1933 // Not (yet) supported in StyleKit
1934 case PE_IndicatorTabClose:
1935 case PE_IndicatorTabTearRight:
1936 case PE_IndicatorTabTearLeft:
1937 return;
1938#endif // QT_CONFIG(tabbar)
1939#if QT_CONFIG(toolbutton)
1940 case PE_FrameButtonTool:
1941 case PE_PanelButtonTool: {
1942 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolButton, opt->state);
1943 if (!r.isValid())
1944 break;
1945 d->drawStyledItemRect(r.background(), opt->rect, p);
1946 return;
1947 }
1948#endif // QT_CONFIG(toolbutton)
1949#if QT_CONFIG(toolbar)
1950 case PE_IndicatorToolBarHandle:
1951 return; // no handle in Controls style
1952 case PE_IndicatorToolBarSeparator: {
1953 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolSeparator, opt->state);
1954 if (!r.isValid())
1955 break;
1956 const auto &metrics = *r.metrics;
1957 // background
1958 QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
1959 d->drawStyledItemRect(r.background(), frameRect, p);
1960 // indicator
1961 const auto *indicator = r.indicator();
1962 if (!indicator || !indicator->visible() || indicator->opacity() == 0)
1963 return;
1964 QRect contentRect = frameRect.marginsRemoved(metrics.padding);
1965 const int iw = QtPrivate::qSaturateRound(
1966 resolvedWidth(indicator,
1967 contentRect.width()
1968 - metrics.indicatorMargins.left()
1969 - metrics.indicatorMargins.right()));
1970 const int ih = QtPrivate::qSaturateRound(
1971 resolvedHeight(indicator,
1972 contentRect.height()
1973 - metrics.indicatorMargins.top()
1974 - metrics.indicatorMargins.bottom()));
1975 const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignHCenter,
1976 Qt::AlignVCenter);
1977 const QRect indicatorRect = d->getAlignedRectInContainer(
1978 contentRect, QSize(iw, ih), alignment, QMargins(), metrics.indicatorMargins);
1979 d->drawControlIndicator(indicator, visualRect(opt->direction, contentRect, indicatorRect),
1980 p);
1981 return;
1982 }
1983#endif
1984 default:
1985 break;
1986 }
1987 QCommonStyle::drawPrimitive(pe, opt, p, w);
1988}
1989
1990/*! \reimp */
1991void QStyleKitStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
1992 const QWidget *w) const
1993{
1994 Q_D(const QStyleKitStyle);
1995
1996 switch (element) {
1997 case CE_PushButton:
1998 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
1999 proxy()->drawControl(CE_PushButtonBevel, btn, p, w);
2000 QStyleOptionButton btnContent(*btn);
2001 btnContent.rect = subElementRect(SE_PushButtonContents, btn, w);
2002 proxy()->drawControl(CE_PushButtonLabel, &btnContent, p, w);
2003 }
2004 return;
2005 case CE_PushButtonBevel:
2006 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2007 QStyleOptionButton btnBg(*btn);
2008 btnBg.rect = subElementRect(SE_PushButtonBevel, btn, w);
2009 proxy()->drawPrimitive(PE_FrameButtonBevel, &btnBg, p, w);
2010 }
2011 return;
2012 case CE_PushButtonLabel:
2013 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2014 const auto controlType = btn->features & QStyleOptionButton::Flat
2015 ? QQStyleKitReader::ControlType::FlatButton
2016 : QQStyleKitReader::ControlType::Button;
2017 const auto r = d->resolve(w, controlType, btn->state);
2018 if (!r.isValid())
2019 break;
2020 const auto metrics = r.metrics;
2021
2022 QRect textRect = opt->rect;
2023 uint textFlags = Qt::TextShowMnemonic;
2024 if (!styleHint(SH_UnderlineShortcut, opt, w))
2025 textFlags |= Qt::TextHideMnemonic;
2026
2027 // icon
2028 const QIcon icon = btn->icon;
2029 if (!icon.isNull()) {
2030 const auto *textProps = r.text();
2031 uint iconTextFlags = textFlags;
2032 iconTextFlags |= textProps
2033 ? resolvedAlignment(textProps->alignment(), Qt::AlignHCenter, Qt::AlignVCenter)
2034 : uint(Qt::AlignHCenter | Qt::AlignVCenter);
2035 QIcon::Mode mode = btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
2036 if (mode == QIcon::Normal && btn->state & State_HasFocus)
2037 mode = QIcon::Active;
2038 QIcon::State iconState = btn->state & State_On ? QIcon::On : QIcon::Off;
2039 const auto paintDeviceDpr = p->device()->devicePixelRatio();
2040 QPixmap pixmap = icon.pixmap(btn->iconSize, paintDeviceDpr, mode, iconState);
2041 int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
2042 int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
2043 int labelWidth = pixmapWidth;
2044 int iconSpacing = metrics->spacing;
2045 int textWidth = btn->fontMetrics
2046 .boundingRect(opt->rect, iconTextFlags, btn->text)
2047 .width();
2048 if (!btn->text.isEmpty()) {
2049 labelWidth += (textWidth
2050 + iconSpacing + textProps->leftPadding() + textProps->rightPadding());
2051 }
2052
2053 QRect iconRect;
2054 if (iconTextFlags & Qt::AlignLeft) {
2055 iconRect = QRect(textRect.x(),
2056 textRect.y() + (textRect.height() - pixmapHeight) / 2,
2057 pixmapWidth, pixmapHeight);
2058 } else if (iconTextFlags & Qt::AlignHCenter) {
2059 iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2,
2060 textRect.y() + (textRect.height() - pixmapHeight) / 2,
2061 pixmapWidth, pixmapHeight);
2062 } else {
2063 iconRect = QRect(textRect.x() + textRect.width() - labelWidth,
2064 textRect.y() + (textRect.height() - pixmapHeight) / 2,
2065 pixmapWidth, pixmapHeight);
2066 }
2067 iconRect = visualRect(btn->direction, textRect, iconRect);
2068
2069 // After placing the icon, left-align the text relative to it
2070 textFlags &= ~Qt::AlignHorizontal_Mask;
2071 textFlags |= Qt::AlignLeft;
2072 if (btn->direction == Qt::RightToLeft)
2073 textRect.setRight(iconRect.left() - iconSpacing);
2074 else
2075 textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing);
2076
2077 p->drawPixmap(iconRect, pixmap);
2078 }
2079
2080 if (btn->features & QStyleOptionButton::HasMenu) {
2081 int indicatorSize = pixelMetric(PM_MenuButtonIndicator, btn, w);
2082 if (btn->direction == Qt::LeftToRight)
2083 textRect = textRect.adjusted(0, 0, -indicatorSize, 0);
2084 else
2085 textRect = textRect.adjusted(indicatorSize, 0, 0, 0);
2086 }
2087 d->drawControlText(r.text(), r.font(), textRect, btn->text, textFlags, p);
2088 return;
2089 }
2090 break;
2091 case CE_CheckBox:
2092 case CE_RadioButton:
2093 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2094 const auto controlType = element == CE_CheckBox
2095 ? QQStyleKitReader::ControlType::CheckBox
2096 : QQStyleKitReader::ControlType::RadioButton;
2097 const auto r = d->resolve(w, controlType, btn->state);
2098 if (!r.isValid())
2099 break;
2100 QRect backgroundRect = btn->rect.marginsRemoved(r.metrics->margins);
2101 // background
2102 d->drawStyledItemRect(r.background(), backgroundRect, p);
2103 // label
2104 QStyleOptionButton btnContent(*btn);
2105 const auto contentElement = element == CE_CheckBox
2106 ? SE_CheckBoxContents
2107 : SE_RadioButtonContents;
2108 btnContent.rect = subElementRect(contentElement, btn, w);
2109 const auto labelElement = element == CE_CheckBox
2110 ? CE_CheckBoxLabel
2111 : CE_RadioButtonLabel;
2112 proxy()->drawControl(labelElement, &btnContent, p, w);
2113 // indicator
2114 QStyleOptionButton indicator(*btn);
2115 const auto indicatorElement = element == CE_CheckBox
2116 ? SE_CheckBoxIndicator
2117 : SE_RadioButtonIndicator;
2118 indicator.rect = subElementRect(indicatorElement, btn, w);
2119 const auto primitiveElement = element == CE_CheckBox
2120 ? PE_IndicatorCheckBox
2121 : PE_IndicatorRadioButton;
2122 proxy()->drawPrimitive(primitiveElement, &indicator, p, w);
2123 return;
2124 }
2125 break;
2126 case CE_CheckBoxLabel:
2127 case CE_RadioButtonLabel:
2128 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2129 const auto controlType = element == CE_CheckBoxLabel
2130 ? QQStyleKitReader::ControlType::CheckBox
2131 : QQStyleKitReader::ControlType::RadioButton;
2132 const auto r = d->resolve(w, controlType, btn->state);
2133 if (!r.isValid())
2134 break;
2135 uint textFlags = Qt::TextShowMnemonic;
2136 if (!styleHint(SH_UnderlineShortcut, opt, w))
2137 textFlags |= Qt::TextHideMnemonic;
2138 d->drawControlText(r.text(), r.font(), opt->rect, btn->text, textFlags, p);
2139 return;
2140 }
2141 break;
2142#if QT_CONFIG(combobox)
2143 case CE_ComboBoxLabel:
2144 if (const auto *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
2145 if (comboBox->editable)
2146 return;
2147 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ComboBox, comboBox->state);
2148 if (!r.isValid())
2149 break;
2150 const QRect textRect = subControlRect(CC_ComboBox, comboBox, SC_ComboBoxEditField, w)
2151 .marginsRemoved(r.metrics->textPadding);
2152 uint textFlags = Qt::TextShowMnemonic;
2153 if (!styleHint(SH_UnderlineShortcut, opt, w))
2154 textFlags |= Qt::TextHideMnemonic;
2155 d->drawControlText(r.text(), r.font(), textRect, comboBox->currentText, textFlags, p);
2156 return;
2157 }
2158 break;
2159#endif // QT_CONFIG(combobox)
2160#if QT_CONFIG(progressbar)
2161 case CE_ProgressBar:
2162 if (const QStyleOptionProgressBar *progressBar =
2163 qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
2164 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ProgressBar,
2165 progressBar->state);
2166 if (!r.isValid())
2167 break;
2168 QRect backgroundRect = progressBar->rect.marginsRemoved(r.metrics->margins);
2169 // background
2170 d->drawStyledItemRect(r.background(), backgroundRect, p);
2171 // groove
2172 QStyleOptionProgressBar contents(*progressBar);
2173 contents.rect = subElementRect(SE_ProgressBarGroove, progressBar, w);
2174 const QRect grooveRect = contents.rect;
2175 proxy()->drawControl(CE_ProgressBarGroove, &contents, p, w);
2176 // track
2177 contents.rect = subElementRect(SE_ProgressBarContents, progressBar, w);
2178 // The track is the groove's foreground child, so it inherits the indicator's
2179 // transform
2180 QPainterStateGuard stateGuard(p);
2181 if (applyDelegateTransform(p, r.indicator(), grooveRect))
2182 proxy()->drawControl(CE_ProgressBarContents, &contents, p, w);
2183 // We intentionally don't draw the label as it is not drawn on the Controls Style
2184 return;
2185 }
2186 break;
2187 case CE_ProgressBarGroove: {
2188 // groove = indicator background
2189 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ProgressBar, opt->state);
2190 if (!r.isValid())
2191 break;
2192 d->drawStyledItemRect(r.indicator(), opt->rect, p);
2193 return;
2194 }
2195 case CE_ProgressBarContents:
2196 // contents = indicator foreground
2197 if (const auto *progressBar = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
2198 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ProgressBar,
2199 progressBar->state);
2200 if (!r.isValid())
2201 break;
2202 const auto progress = progressBar->progress;
2203 const auto ratio = progressBar->maximum > progressBar->minimum
2204 ? (progress - progressBar->minimum)
2205 / static_cast<qreal>(progressBar->maximum - progressBar->minimum)
2206 : 0;
2207 const auto width = static_cast<int>(progressBar->rect.width() * ratio);
2208 const auto x = progressBar->invertedAppearance
2209 ? progressBar->rect.right() - width
2210 : progressBar->rect.left();
2211 const auto contentsRect = QRect(x, progressBar->rect.y(),
2212 width, progressBar->rect.height());
2213 const auto *foreground = r.indicator() ? r.indicator()->foreground() : nullptr;
2214 if (foreground && foreground->visible() && foreground->opacity() > 0) {
2215 QPainterStateGuard stateGuard(p);
2216 if (applyDelegateTransform(p, foreground, progressBar->rect))
2217 d->drawStyledItemContents(foreground, contentsRect, p);
2218 }
2219 return;
2220 }
2221 break;
2222#endif // QT_CONFIG(progressbar)
2223#if QT_CONFIG(itemviews)
2224 case CE_ItemViewItem:
2225 if (const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
2226 const auto r = d->resolveSubElement(w, opt, itemViewControlType(opt), opt->state);
2227 if (!r.isValid())
2228 break;
2229 QStyleOptionViewItem optBg(*itemViewOption);
2230 optBg.rect = optBg.rect.marginsRemoved(r.metrics->margins);
2231 QRect indicatorRect = subElementRect(SE_ItemViewItemCheckIndicator, itemViewOption, w);
2232 QRect iconRect = subElementRect(SE_ItemViewItemDecoration, itemViewOption, w);
2233 QRect textRect = subElementRect(SE_ItemViewItemText, itemViewOption, w);
2234 // Capture text properties + font now, before the indicator
2235 // drawPrimitive below potentially mutates the reader's state
2236 const auto *itemTextProps = r.text();
2237 const QFont itemFont = r.font();
2238
2239 // background
2240 proxy()->drawPrimitive(PE_PanelItemViewItem, &optBg, p, w);
2241
2242 // indicator
2243 if (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator) {
2244 QStyleOptionViewItem option(*itemViewOption);
2245 option.rect = indicatorRect;
2246 option.state = option.state & ~QStyle::State_HasFocus;
2247
2248 switch (itemViewOption->checkState) {
2249 case Qt::Unchecked:
2250 option.state |= QStyle::State_Off;
2251 break;
2252 case Qt::PartiallyChecked:
2253 option.state |= QStyle::State_NoChange;
2254 break;
2255 case Qt::Checked:
2256 option.state |= QStyle::State_On;
2257 break;
2258 }
2259 proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, p, w);
2260 }
2261
2262 // icon
2263 QIcon::Mode mode = QIcon::Normal;
2264 if (!(itemViewOption->state & QStyle::State_Enabled))
2265 mode = QIcon::Disabled;
2266 else if (itemViewOption->state & QStyle::State_Selected)
2267 mode = QIcon::Selected;
2268 QIcon::State state = itemViewOption->state & QStyle::State_Open
2269 ? QIcon::On
2270 : QIcon::Off;
2271 itemViewOption->icon.paint(p, iconRect, itemViewOption->decorationAlignment, mode,
2272 state);
2273
2274 // draw text
2275 uint textFlags = Qt::TextShowMnemonic;
2276 if (!styleHint(SH_UnderlineShortcut, opt, w))
2277 textFlags |= Qt::TextHideMnemonic;
2278 d->drawControlText(itemTextProps, itemFont, textRect, itemViewOption->text, textFlags,
2279 p, Qt::AlignLeft | Qt::AlignVCenter);
2280 return;
2281 }
2282 break;
2283#endif // QT_CONFIG(itemviews)
2284#ifndef QT_NO_FRAME
2285 case CE_ShapedFrame: {
2286# if QT_CONFIG(scrollarea)
2287 if (qobject_cast<const QAbstractScrollArea *>(w))
2288 // ScrollView has no styling in Controls, keep consistent
2289 return;
2290# endif
2291 auto controlType = QQStyleKitReader::ControlType::Frame;
2292# if QT_CONFIG(combobox)
2293 if (w && w->inherits("QComboBoxPrivateContainer"))
2294 controlType = QQStyleKitReader::ControlType::Popup;
2295# endif
2296# if QT_CONFIG(textedit)
2297 else if (w && qobject_cast<const QTextEdit *>(w))
2298 controlType = QQStyleKitReader::ControlType::TextArea;
2299# endif
2300# if QT_CONFIG(label)
2301 else if (w && qobject_cast<const QLabel *>(w))
2302 controlType = QQStyleKitReader::ControlType::Label;
2303# endif
2304 // Fallback to Frame
2305 if (qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
2306 const auto r = d->resolve(w, controlType, opt->state);
2307 if (!r.isValid())
2308 break;
2309 QRect backgroundRect = opt->rect.marginsRemoved(r.metrics->margins);
2310 d->drawStyledItemRect(r.background(), backgroundRect, p);
2311 return;
2312 }
2313 break;
2314 }
2315#endif // QT_NO_FRAME
2316#if QT_CONFIG(scrollbar)
2317 case CE_ScrollBarSlider: {
2318 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ScrollBar, opt->state);
2319 if (!r.isValid())
2320 break;
2321 d->drawControlIndicator(r.indicator(), opt->rect, p);
2322 return;
2323 }
2324#endif // QT_CONFIG(scrollbar)
2325#if QT_CONFIG(toolbutton)
2326 case CE_ToolButtonLabel:
2327 if (const auto *tool = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
2328 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolButton, tool->state);
2329 if (!r.isValid())
2330 break;
2331 const auto *metrics = r.metrics;
2332 QRect rect = tool->rect.marginsRemoved(metrics->textPadding);
2333
2334 uint textFlags = Qt::TextShowMnemonic;
2335 if (!styleHint(SH_UnderlineShortcut, opt, w))
2336 textFlags |= Qt::TextHideMnemonic;
2337
2338 const bool hasArrow = tool->features & QStyleOptionToolButton::Arrow;
2339 if ((!hasArrow && tool->icon.isNull() && !tool->text.isEmpty())
2340 || tool->toolButtonStyle == Qt::ToolButtonTextOnly) {
2341 d->drawControlText(r.text(), r.font(), rect, tool->text, textFlags, p);
2342 return;
2343 }
2344
2345 QPixmap pm;
2346 QSize pmSize;
2347 if (!hasArrow && !tool->icon.isNull()) {
2348 const QIcon::State iconState = tool->state & State_On ? QIcon::On : QIcon::Off;
2349 QIcon::Mode mode;
2350 if (!(tool->state & State_Enabled))
2351 mode = QIcon::Disabled;
2352 else if ((tool->state & State_MouseOver) && (tool->state & State_AutoRaise))
2353 mode = QIcon::Active;
2354 else
2355 mode = QIcon::Normal;
2356 pm = tool->icon.pixmap(tool->iconSize, p->device()->devicePixelRatio(), mode,
2357 iconState);
2358 pmSize = pm.size() / pm.devicePixelRatio();
2359 }
2360
2361 if (tool->toolButtonStyle == Qt::ToolButtonIconOnly || tool->text.isEmpty()) {
2362 if (!pm.isNull()) {
2363 p->drawPixmap(visualRect(tool->direction, rect,
2364 QRect(rect.x() + (rect.width() - pmSize.width()) / 2,
2365 rect.y() + (rect.height() - pmSize.height()) / 2,
2366 pmSize.width(), pmSize.height())),
2367 pm);
2368 }
2369 return;
2370 }
2371
2372 if (tool->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
2373 if (!pm.isNull()) {
2374 const QRect pr(rect.x() + (rect.width() - pmSize.width()) / 2, rect.y(),
2375 pmSize.width(), pmSize.height());
2376 p->drawPixmap(visualRect(tool->direction, rect, pr), pm);
2377 QRect tr = rect;
2378 tr.setTop(rect.y() + pmSize.height() + metrics->spacing);
2379 d->drawControlText(r.text(), r.font(), visualRect(tool->direction, rect, tr),
2380 tool->text, textFlags, p, Qt::AlignCenter);
2381 } else {
2382 d->drawControlText(r.text(), r.font(), rect, tool->text, textFlags, p,
2383 Qt::AlignCenter);
2384 }
2385 } else {
2386 if (!pm.isNull()) {
2387 const QRect pr(rect.x(), rect.y() + (rect.height() - pmSize.height()) / 2,
2388 pmSize.width(), pmSize.height());
2389 p->drawPixmap(visualRect(tool->direction, rect, pr), pm);
2390 QRect tr = rect;
2391 tr.setLeft(rect.x() + pmSize.width() + metrics->spacing);
2392 d->drawControlText(r.text(), r.font(), visualRect(tool->direction, rect, tr),
2393 tool->text, textFlags, p, Qt::AlignLeft | Qt::AlignVCenter);
2394 } else {
2395 d->drawControlText(r.text(), r.font(), rect, tool->text, textFlags, p,
2396 Qt::AlignLeft | Qt::AlignVCenter);
2397 }
2398 }
2399 return;
2400 }
2401 break;
2402#endif // QT_CONFIG(toolbutton)
2403#if QT_CONFIG(menu)
2404 case CE_MenuItem:
2405 if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
2406 using QtPrivate::qSaturateRound;
2407 // MenuItemSeperator
2408 if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
2409 const auto r = d->resolve(w, QQStyleKitReader::ControlType::MenuSeparator,
2410 menuItem->state);
2411 if (!r.isValid())
2412 break;
2413 const auto metrics = r.metrics;
2414 QRect contentRect = opt->rect
2415 .marginsRemoved(metrics->margins)
2416 .marginsRemoved(metrics->padding);
2417 const auto *indicator = r.indicator();
2418 if (indicator && indicator->visible() && indicator->opacity() > 0) {
2419 const int w = qSaturateRound(resolvedWidth(
2420 indicator,
2421 contentRect.width() - indicator->leftMargin() - indicator->rightMargin()));
2422 const int h = qSaturateRound(resolvedHeight(
2423 indicator,
2424 contentRect.height() - indicator->topMargin() - indicator->bottomMargin()));
2425 const uint align = resolvedAlignment(indicator->alignment(), Qt::AlignHCenter,
2426 Qt::AlignVCenter);
2427 const QRect indicatorRect = d->getAlignedRectInContainer(
2428 contentRect, QSize(w, h), align, QMargins(), elementMargins(indicator));
2429 d->drawControlIndicator(
2430 indicator, visualRect(opt->direction, contentRect, indicatorRect), p);
2431 }
2432 return;
2433 }
2434 // MenuItem
2435 QStyle::State adjustedState = menuItem->state;
2436 if (adjustedState & State_Selected)
2437 adjustedState |= State_MouseOver;
2438
2439 const auto r = d->resolveSubElement(w, opt, QQStyleKitReader::ControlType::MenuItem,
2440 adjustedState);
2441 if (!r.isValid())
2442 break;
2443
2444 // background
2445 d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
2446
2447 // indicators
2448 const QRect contentRect = opt->rect.marginsRemoved(r.metrics->padding);
2449 const auto *indicator = r.indicator();
2450 const auto *first = indicator ? indicator->first() : nullptr;
2451 const auto *second = indicator ? indicator->second() : nullptr;
2452 int leftOffset = 0, rightOffset = 0;
2453 const int spacing = r.metrics->spacing;
2454 QRect firstDrawRect, secondDrawRect;
2455 auto placeSubIndicator = [&](const QQStyleKitIndicatorProperties *sub,
2456 QRect &drawRect) {
2457 if (!sub)
2458 return;
2459 const QSize sz(qSaturateRound(sub->width()), qSaturateRound(sub->height()));
2460 const QMargins subMargins = elementMargins(sub);
2461 const int slotW = qSaturateRound(sub->leftMargin() + sub->width() + sub->rightMargin());
2462 if (sub->alignment() & Qt::AlignRight) {
2463 const QRect slot(contentRect.right() - rightOffset - slotW, contentRect.top(),
2464 slotW, contentRect.height());
2465 drawRect = d->getAlignedRectInContainer(
2466 slot, sz, Qt::AlignRight | Qt::AlignVCenter, { }, subMargins);
2467 rightOffset += slotW + spacing;
2468 } else {
2469 const QRect slot(contentRect.left() + leftOffset, contentRect.top(), slotW,
2470 contentRect.height());
2471 drawRect = d->getAlignedRectInContainer(
2472 slot, sz, Qt::AlignLeft | Qt::AlignVCenter, { }, subMargins);
2473 leftOffset += slotW + spacing;
2474 }
2475 };
2476 placeSubIndicator(first, firstDrawRect);
2477 placeSubIndicator(second, secondDrawRect);
2478
2479 if (first && menuItem->checkType != QStyleOptionMenuItem::NotCheckable
2480 && menuItem->checked) {
2481 d->drawControlIndicator(
2482 first, visualRect(menuItem->direction, contentRect, firstDrawRect), p);
2483 }
2484
2485 if (second && menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {
2486 d->drawControlIndicator(
2487 second, visualRect(menuItem->direction, contentRect, secondDrawRect), p);
2488 }
2489
2490 // text
2491 QRect textRect = contentRect
2492 .adjusted(leftOffset, 0, -rightOffset, 0)
2493 .marginsRemoved(r.metrics->textPadding);
2494 if (!menuItem->icon.isNull()) {
2495 const int iconW = menuItem->maxIconWidth;
2496 const QIcon::Mode mode = (menuItem->state & State_Enabled)
2497 ? QIcon::Normal
2498 : QIcon::Disabled;
2499 const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, opt, w);
2500 const QPixmap pm = menuItem->icon.pixmap(
2501 QSize(iconExtent, iconExtent), p->device()->devicePixelRatio(), mode);
2502 const QSize pmSize = pm.size() / pm.devicePixelRatio();
2503 const int pmW = pmSize.width();
2504 const int pmH = pmSize.height();
2505 p->drawPixmap(
2506 visualRect(menuItem->direction, textRect,
2507 QRect(textRect.left(),
2508 textRect.top() + (textRect.height() - pmH) / 2, pmW, pmH)),
2509 pm);
2510 textRect.setLeft(textRect.left() + iconW + r.metrics->spacing);
2511 }
2512
2513 const int tabIdx = menuItem->text.indexOf(QLatin1Char('\t'));
2514 const QString label = tabIdx >= 0 ? menuItem->text.left(tabIdx) : menuItem->text;
2515 const QString shortcut = tabIdx >= 0 ? menuItem->text.mid(tabIdx + 1) : QString();
2516 uint textFlags = Qt::TextShowMnemonic;
2517 if (!styleHint(SH_UnderlineShortcut, opt, w))
2518 textFlags |= Qt::TextHideMnemonic;
2519 QRect labelRect = textRect;
2520 if (!shortcut.isEmpty() && menuItem->reservedShortcutWidth > 0) {
2521 const QRect shortcutRect(textRect.right() - menuItem->reservedShortcutWidth + 1,
2522 textRect.top(), menuItem->reservedShortcutWidth,
2523 textRect.height());
2524 labelRect.setRight(shortcutRect.left() - r.metrics->spacing - 1);
2525 const int shortcutFlags =
2526 int(QStyle::visualAlignment(menuItem->direction,
2527 Qt::AlignRight | Qt::AlignVCenter))
2528 | int(textFlags);
2529 p->save();
2530 p->setFont(r.font().resolve(p->font()));
2531 p->setBrush(Qt::NoBrush);
2532 if (const auto *shortcutText = r.text())
2533 p->setPen(shortcutText->color());
2534 p->drawText(visualRect(menuItem->direction, textRect, shortcutRect), shortcutFlags,
2535 shortcut);
2536 p->restore();
2537 }
2538 d->drawControlText(r.text(), r.font(),
2539 visualRect(menuItem->direction, textRect, labelRect), label,
2540 textFlags, p, Qt::AlignLeft | Qt::AlignVCenter);
2541 return;
2542 }
2543 break;
2544 // no tearoff or bottom scroller support in Controls style,
2545 // so make it consistent and don't draw them
2546 case CE_MenuScroller:
2547 case CE_MenuTearoff:
2548 return;
2549 case CE_MenuEmptyArea:
2550 return;
2551#endif // QT_CONFIG(menu)
2552#if QT_CONFIG(menubar)
2553 case CE_MenuBarEmptyArea:
2554 return;
2555 case CE_MenuBarItem:
2556 if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
2557 QStyle::State adjustedState = menuItem->state;
2558 if (adjustedState & State_Selected)
2559 adjustedState |= State_MouseOver;
2560 const auto r = d->resolveSubElement(w, opt, QQStyleKitReader::ControlType::MenuBarItem,
2561 adjustedState);
2562 if (!r.isValid())
2563 break;
2564 d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
2565 const QRect textRect = opt->rect.marginsRemoved(
2566 r.metrics->margins + r.metrics->padding + r.metrics->textPadding);
2567 uint textFlags = Qt::TextShowMnemonic;
2568 if (!styleHint(SH_UnderlineShortcut, opt, w))
2569 textFlags |= Qt::TextHideMnemonic;
2570 d->drawControlText(r.text(), r.font(), textRect, menuItem->text, textFlags, p);
2571 return;
2572 }
2573 break;
2574#endif // QT_CONFIG(menubar)
2575#if QT_CONFIG(tabbar)
2576 case CE_TabBarTab:
2577 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
2578 const auto r = d->resolveSubElement(w, opt, QQStyleKitReader::ControlType::TabButton,
2579 tab->state);
2580 if (!r.isValid())
2581 break;
2582 const auto &m = *r.metrics;
2583
2584 const QRect backgroundRect = opt->rect.marginsRemoved(m.margins);
2585 d->drawStyledItemRect(r.background(), backgroundRect, p);
2586
2587 const QRect contentRect = backgroundRect.marginsRemoved(m.padding);
2588 uint textFlags = Qt::TextShowMnemonic;
2589 if (!styleHint(SH_UnderlineShortcut, opt, w))
2590 textFlags |= Qt::TextHideMnemonic;
2591
2592 if (!tab->icon.isNull()) {
2593 const QIcon::Mode mode = (tab->state & State_Enabled)
2594 ? QIcon::Normal
2595 : QIcon::Disabled;
2596 const QIcon::State iconState = (tab->state & State_Selected)
2597 ? QIcon::On
2598 : QIcon::Off;
2599 const QPixmap pm = tab->icon.pixmap(tab->iconSize, p->device()->devicePixelRatio(),
2600 mode, iconState);
2601 const QSize pmSize = pm.size() / pm.devicePixelRatio();
2602 const QRect pr(contentRect.x(),
2603 contentRect.y() + (contentRect.height() - pmSize.height()) / 2,
2604 pmSize.width(), pmSize.height());
2605 p->drawPixmap(visualRect(tab->direction, contentRect, pr), pm);
2606 QRect tr = contentRect;
2607 tr.setLeft(contentRect.x() + pmSize.width() + m.spacing);
2608 d->drawControlText(r.text(), r.font(), visualRect(tab->direction, contentRect, tr),
2609 tab->text, textFlags, p, Qt::AlignCenter);
2610 } else {
2611 d->drawControlText(r.text(), r.font(), contentRect, tab->text, textFlags, p,
2612 Qt::AlignCenter);
2613 }
2614 return;
2615 }
2616 break;
2617#endif // QT_CONFIG(tabbar)
2618#if QT_CONFIG(toolbar)
2619 case CE_ToolBar:
2620 if (const auto *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
2621 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolBar, toolBar->state);
2622 if (!r.isValid())
2623 break;
2624 d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
2625 return;
2626 }
2627 break;
2628#endif // QT_CONFIG(toolbar)
2629 default:
2630 break;
2631 }
2632 QCommonStyle::drawControl(element, opt, p, w);
2633}
2634
2635/*! \reimp */
2636QRect QStyleKitStyle::subElementRect(SubElement r, const QStyleOption *opt,
2637 const QWidget *widget) const
2638{
2639 using QtPrivate::qSaturateRound;
2640 Q_D(const QStyleKitStyle);
2641
2642 switch (r) {
2643 case SE_PushButtonLayoutItem:
2644 case SE_PushButtonBevel:
2645 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2646 const auto controlType = btn->features & QStyleOptionButton::Flat
2647 ? QQStyleKitReader::ControlType::FlatButton
2648 : QQStyleKitReader::ControlType::Button;
2649 const auto resolved = d->resolveLayout(controlType, opt->state);
2650 if (!resolved.isValid())
2651 break;
2652 const auto &metrics = *resolved.metrics;
2653 QRect rect = opt->rect.marginsRemoved(metrics.margins);
2654 return visualRect(opt->direction, opt->rect, rect);
2655 }
2656 break;
2657 case SE_PushButtonContents:
2658 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2659 const auto controlType = btn->features & QStyleOptionButton::Flat
2660 ? QQStyleKitReader::ControlType::FlatButton
2661 : QQStyleKitReader::ControlType::Button;
2662 const auto resolved = d->resolveLayout(controlType, opt->state);
2663 if (!resolved.isValid())
2664 break;
2665 const auto &metrics = *resolved.metrics;
2666 QRect rect = opt->rect.marginsRemoved(
2667 metrics.margins + metrics.padding + metrics.textPadding);
2668 return visualRect(opt->direction, opt->rect, rect);
2669 }
2670 break;
2671 case SE_CheckBoxContents:
2672 case SE_RadioButtonContents: {
2673 const auto controlType = r == SE_CheckBoxContents
2674 ? QQStyleKitReader::ControlType::CheckBox
2675 : QQStyleKitReader::ControlType::RadioButton;
2676 const auto resolved = d->resolveLayout(controlType, opt->state);
2677 if (!resolved.isValid())
2678 break;
2679 const auto &metrics = *resolved.metrics;
2680 QRect contentsRect = opt->rect.marginsRemoved(metrics.margins + metrics.padding);
2681 const auto subElement = r == SE_CheckBoxContents
2682 ? SE_CheckBoxIndicator
2683 : SE_RadioButtonIndicator;
2684 QRect indicatorRect = visualRect(opt->direction, opt->rect,
2685 subElementRect(subElement, opt, widget));
2686 const int spacing = metrics.spacing;
2687 const auto *indicator = resolved.indicator();
2688 const uint alignment = indicator
2689 ? resolvedAlignment(indicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
2690 : uint(Qt::AlignLeft | Qt::AlignVCenter);
2691 if (alignment & Qt::AlignLeft) {
2692 contentsRect.setLeft(indicatorRect.right() + spacing + metrics.textPadding.left());
2693 } else if (alignment & Qt::AlignHCenter) {
2694 contentsRect.setLeft(indicatorRect.right() + spacing + metrics.textPadding.left());
2695 contentsRect.setRight(indicatorRect.left() - spacing - metrics.textPadding.right());
2696 } else {
2697 contentsRect.setRight(indicatorRect.left() - spacing - metrics.textPadding.right());
2698 }
2699 return visualRect(opt->direction, opt->rect, contentsRect);
2700 }
2701 case SE_CheckBoxIndicator:
2702 case SE_RadioButtonIndicator: {
2703 const auto controlType = r == SE_CheckBoxIndicator
2704 ? QQStyleKitReader::ControlType::CheckBox
2705 : QQStyleKitReader::ControlType::RadioButton;
2706 const auto resolved = d->resolveLayout(controlType, opt->state);
2707 if (!resolved.isValid())
2708 break;
2709 const auto &metrics = *resolved.metrics;
2710 QRect rect = opt->rect.marginsRemoved(metrics.margins);
2711 const auto *indicator = resolved.indicator();
2712 if (!indicator || !indicator->visible() || indicator->opacity() == 0)
2713 return rect;
2714
2715 const int w = qSaturateRound(resolvedWidth(
2716 indicator,
2717 rect.width() - metrics.padding.left() - metrics.padding.right()
2718 - metrics.indicatorMargins.left() - metrics.indicatorMargins.right()));
2719 const int h = qSaturateRound(resolvedHeight(
2720 indicator,
2721 rect.height()
2722 - metrics.padding.top() - metrics.padding.bottom()
2723 - metrics.indicatorMargins.top() - metrics.indicatorMargins.bottom()));
2724 const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignLeft,
2725 Qt::AlignVCenter);
2726 const QRect indicatorRect = d->getAlignedRectInContainer(
2727 rect, QSize(w, h), alignment, metrics.padding, metrics.indicatorMargins);
2728 return visualRect(opt->direction, rect, indicatorRect);
2729 }
2730#if QT_CONFIG(itemviews)
2731 case SE_ItemViewItemCheckIndicator: {
2732 const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
2733 if (!resolved.isValid())
2734 break;
2735 const auto &metrics = *resolved.metrics;
2736 QRect rect = opt->rect.marginsRemoved(metrics.margins);
2737 const auto *indicator = resolved.indicator();
2738 if (!indicator || !indicator->visible() || indicator->opacity() == 0)
2739 return rect;
2740
2741 const int w = qSaturateRound(resolvedWidth(
2742 indicator,
2743 rect.width()
2744 - metrics.padding.left() - metrics.padding.right()
2745 - metrics.indicatorMargins.left() - metrics.indicatorMargins.right()));
2746 const int h = qSaturateRound(resolvedHeight(
2747 indicator,
2748 rect.height() - metrics.padding.top() - metrics.padding.bottom()
2749 - metrics.indicatorMargins.top() - metrics.indicatorMargins.bottom()));
2750 const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignLeft,
2751 Qt::AlignVCenter);
2752 const QRect indicatorRect = d->getAlignedRectInContainer(
2753 rect, QSize(w, h), alignment, metrics.padding, metrics.indicatorMargins);
2754 return visualRect(opt->direction, rect, indicatorRect);
2755 }
2756 case SE_ItemViewItemDecoration:
2757 if (const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
2758 if (!(itemViewOption->features & QStyleOptionViewItem::HasDecoration))
2759 return QRect();
2760 const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
2761 if (!resolved.isValid())
2762 break;
2763 const auto &metrics = *resolved.metrics;
2764 QRect contentsRect = opt->rect.marginsRemoved(metrics.margins + metrics.padding);
2765 if (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator) {
2766 const QRect indicatorRect = subElementRect(
2767 SE_ItemViewItemCheckIndicator, opt, widget);
2768 const auto *checkIndicator = resolved.indicator();
2769 const uint checkAlign = checkIndicator
2770 ? resolvedAlignment(checkIndicator->alignment(), Qt::AlignLeft,
2771 Qt::AlignVCenter)
2772 : uint(Qt::AlignLeft | Qt::AlignVCenter);
2773 if (checkAlign & Qt::AlignLeft) {
2774 contentsRect.setLeft(indicatorRect.right()
2775 + metrics.indicatorMargins.right() + metrics.spacing);
2776 } else if (checkAlign & Qt::AlignRight) {
2777 contentsRect.setRight(indicatorRect.left()
2778 - metrics.indicatorMargins.left() - metrics.spacing);
2779 }
2780 }
2781
2782 const QSize decorationSize = itemViewOption->decorationSize
2783 .boundedTo(contentsRect.size());
2784 QRect decorationRect;
2785 switch (itemViewOption->decorationPosition) {
2786 case QStyleOptionViewItem::Top:
2787 decorationRect = QRect(contentsRect.left(), contentsRect.top(),
2788 contentsRect.width(), decorationSize.height());
2789 break;
2790 case QStyleOptionViewItem::Bottom:
2791 decorationRect = QRect(contentsRect.left(),
2792 contentsRect.bottom() - decorationSize.height() + 1,
2793 contentsRect.width(), decorationSize.height());
2794 break;
2795 case QStyleOptionViewItem::Right:
2796 decorationRect = QRect(contentsRect.right() - decorationSize.width() + 1,
2797 contentsRect.top(), decorationSize.width(),
2798 contentsRect.height());
2799 break;
2800 case QStyleOptionViewItem::Left:
2801 default:
2802 decorationRect = QRect(contentsRect.left(), contentsRect.top(),
2803 decorationSize.width(), contentsRect.height());
2804 break;
2805 }
2806 const uint decorationAlign = resolvedAlignment(itemViewOption->decorationAlignment,
2807 Qt::AlignLeft, Qt::AlignVCenter);
2808 decorationRect = d->getAlignedRectInContainer(
2809 decorationRect, decorationSize, decorationAlign, QMargins(0, 0, 0, 0),
2810 QMargins(0, 0, 0, 0));
2811 return visualRect(opt->direction, opt->rect, decorationRect);
2812 }
2813 break;
2814 case SE_ItemViewItemText:
2815 if (const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
2816 const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
2817 if (!resolved.isValid())
2818 break;
2819 const auto &metrics = *resolved.metrics;
2820 QRect contentsRect = opt->rect.marginsRemoved(metrics.margins + metrics.padding);
2821 const int spacing = metrics.spacing;
2822
2823 if (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator) {
2824 const QRect indicatorRect = subElementRect(
2825 SE_ItemViewItemCheckIndicator, opt, widget);
2826 const auto *checkIndicator = resolved.indicator();
2827 const uint checkAlign = checkIndicator
2828 ? resolvedAlignment(checkIndicator->alignment(), Qt::AlignLeft,
2829 Qt::AlignVCenter)
2830 : uint(Qt::AlignLeft | Qt::AlignVCenter);
2831 if (checkAlign & Qt::AlignLeft) {
2832 contentsRect.setLeft(
2833 indicatorRect.right() + metrics.indicatorMargins.right() + spacing);
2834 } else if (checkAlign & Qt::AlignRight) {
2835 contentsRect.setRight(
2836 indicatorRect.left() - metrics.indicatorMargins.left() - spacing);
2837 }
2838 }
2839
2840 QRect textRect = contentsRect;
2841 if (itemViewOption->features & QStyleOptionViewItem::HasDecoration) {
2842 const QRect decorationRect = subElementRect(
2843 SE_ItemViewItemDecoration, opt, widget);
2844 switch (itemViewOption->decorationPosition) {
2845 case QStyleOptionViewItem::Top:
2846 textRect.setTop(decorationRect.bottom() + spacing);
2847 break;
2848 case QStyleOptionViewItem::Bottom:
2849 textRect.setBottom(decorationRect.top() - spacing);
2850 break;
2851 case QStyleOptionViewItem::Right:
2852 textRect.setRight(decorationRect.left() - spacing);
2853 break;
2854 case QStyleOptionViewItem::Left:
2855 default:
2856 textRect.setLeft(decorationRect.right() + spacing);
2857 break;
2858 }
2859 }
2860
2861 const auto *textProps = resolved.text();
2862 const uint textAlign = textProps
2863 ? resolvedAlignment(textProps->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
2864 : uint(Qt::AlignLeft | Qt::AlignVCenter);
2865 if (textAlign & Qt::AlignLeft) {
2866 textRect.setLeft(textRect.left() + metrics.textPadding.left());
2867 } else if (textAlign & Qt::AlignHCenter) {
2868 textRect.setLeft(textRect.left() + metrics.textPadding.left() / 2);
2869 textRect.setRight(textRect.right() - metrics.textPadding.right() / 2);
2870 } else {
2871 textRect.setRight(textRect.right() - metrics.textPadding.right());
2872 }
2873 if (textAlign & Qt::AlignTop) {
2874 textRect.setTop(textRect.top() + metrics.textPadding.top());
2875 } else if (textAlign & Qt::AlignVCenter) {
2876 textRect.setTop(textRect.top() + metrics.textPadding.top() / 2);
2877 textRect.setBottom(textRect.bottom() - metrics.textPadding.bottom() / 2);
2878 } else {
2879 textRect.setBottom(textRect.bottom() - metrics.textPadding.bottom());
2880 }
2881 return visualRect(opt->direction, opt->rect, textRect);
2882 }
2883 break;
2884#endif // QT_CONFIG(itemviews)
2885 case SE_PushButtonFocusRect:
2886 case SE_CheckBoxClickRect:
2887 case SE_RadioButtonClickRect:
2888 return opt->rect;
2889#if QT_CONFIG(progressbar)
2890 case SE_ProgressBarGroove: {
2891 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ProgressBar,
2892 opt->state);
2893 if (!resolved.isValid())
2894 break;
2895 const auto &metrics = *resolved.metrics;
2896 QRect rect = opt->rect.marginsRemoved(metrics.margins);
2897 const auto *indicator = resolved.indicator();
2898 if (!indicator || !indicator->visible() || indicator->opacity() == 0)
2899 return rect;
2900
2901 const int w = qSaturateRound(resolvedWidth(
2902 indicator,
2903 rect.width() - metrics.padding.left() - metrics.padding.right()
2904 - metrics.indicatorMargins.left() - metrics.indicatorMargins.right()));
2905 const int h = qSaturateRound(resolvedHeight(
2906 indicator,
2907 rect.height() - metrics.padding.top() - metrics.padding.bottom()
2908 - metrics.indicatorMargins.top() - metrics.indicatorMargins.bottom()));
2909 const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignLeft,
2910 Qt::AlignVCenter);
2911 const QRect indicatorRect = d->getAlignedRectInContainer(
2912 rect, QSize(w, h), alignment, metrics.padding, metrics.indicatorMargins);
2913 return visualRect(opt->direction, rect, indicatorRect);
2914 }
2915 case SE_ProgressBarContents: {
2916 if (qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
2917 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ProgressBar,
2918 opt->state);
2919 if (!resolved.isValid())
2920 break;
2921 const auto *foreground = resolved.indicator()
2922 ? resolved.indicator()->foreground()
2923 : nullptr;
2924 if (!foreground || !foreground->visible() || foreground->opacity() == 0)
2925 return QRect();
2926
2927 QRect indicatorRect = subElementRect(SE_ProgressBarGroove, opt, widget);
2928 const int foregroundW = qSaturateRound(resolvedWidth(
2929 foreground,
2930 indicatorRect.width() - foreground->leftMargin() - foreground->rightMargin()));
2931 const int foregroundH = qSaturateRound(resolvedHeight(
2932 foreground,
2933 indicatorRect.height() - foreground->topMargin() - foreground->bottomMargin()));
2934 const uint foregroundAlign = resolvedAlignment(foreground->alignment(), Qt::AlignLeft, Qt::AlignVCenter);
2935 const QMargins foregroundMargins = elementMargins(foreground);
2936 const QRect foregroundRect = d->getAlignedRectInContainer(
2937 indicatorRect, QSize(foregroundW, foregroundH), foregroundAlign,
2938 QMargins(0, 0, 0, 0), foregroundMargins);
2939 return visualRect(opt->direction, opt->rect, foregroundRect);
2940 }
2941 break;
2942 }
2943#endif // QT_CONFIG(progressbar)
2944 case SE_LineEditContents:
2945 if (qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
2946#if QT_CONFIG(spinbox)
2947 const bool isInSpinBox = widget && qobject_cast<const QSpinBox *>(widget->parentWidget());
2948#else
2949 const bool isInSpinBox = false;
2950#endif
2951 const auto controlType = isInSpinBox ? QQStyleKitReader::ControlType::SpinBox
2952 : QQStyleKitReader::ControlType::TextField;
2953 const auto resolved = d->resolveLayout(controlType, opt->state);
2954 if (!resolved.isValid())
2955 break;
2956 const auto &metrics = *resolved.metrics;
2957 QRect contentsRect = opt->rect.marginsRemoved(metrics.padding);
2958 const auto *textProps = resolved.text();
2959 const uint textAlign = resolvedAlignment(textProps ? textProps->alignment() : 0u,
2960 Qt::AlignLeft, Qt::AlignVCenter);
2961 if (textAlign & Qt::AlignLeft)
2962 contentsRect.setLeft(contentsRect.left() + metrics.textPadding.left());
2963 else if (textAlign & Qt::AlignHCenter) {
2964 contentsRect.setLeft(contentsRect.left() + metrics.textPadding.left() / 2);
2965 contentsRect.setRight(contentsRect.right() - metrics.textPadding.right() / 2);
2966 } else {
2967 contentsRect.setRight(contentsRect.right() - metrics.textPadding.right());
2968 }
2969 if (textAlign & Qt::AlignTop)
2970 contentsRect.setTop(contentsRect.top() + metrics.textPadding.top());
2971 else if (textAlign & Qt::AlignVCenter) {
2972 contentsRect.setTop(contentsRect.top() + metrics.textPadding.top() / 2);
2973 contentsRect.setBottom(contentsRect.bottom() - metrics.textPadding.bottom() / 2);
2974 } else {
2975 contentsRect.setBottom(contentsRect.bottom() - metrics.textPadding.bottom());
2976 }
2977 return visualRect(opt->direction, opt->rect, contentsRect);
2978 }
2979 break;
2980 case SE_ShapedFrameContents:
2981#if QT_CONFIG(combobox)
2982 if (widget && widget->inherits("QComboBoxPrivateContainer")) {
2983 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Popup,
2984 opt->state);
2985 if (!resolved.isValid())
2986 break;
2987 const auto &metrics = *resolved.metrics;
2988 QRect contentsRect = opt->rect.marginsRemoved(
2989 metrics.margins + metrics.padding + metrics.textPadding);
2990 return visualRect(opt->direction, opt->rect, contentsRect);
2991 }
2992#endif
2993#if QT_CONFIG(textedit)
2994 if (qobject_cast<const QTextEdit *>(widget)) {
2995 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::TextArea,
2996 opt->state);
2997 if (!resolved.isValid())
2998 break;
2999 const auto &metrics = *resolved.metrics;
3000 QRect contentsRect = opt->rect.marginsRemoved(
3001 metrics.margins + metrics.padding + metrics.textPadding);
3002 return visualRect(opt->direction, opt->rect, contentsRect);
3003 }
3004#endif // textedit
3005 break;
3006#if QT_CONFIG(tabbar)
3007 case SE_TabBarTabText:
3008 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
3009 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::TabButton,
3010 opt->state);
3011 if (!resolved.isValid())
3012 break;
3013 const auto &metrics = *resolved.metrics;
3014 QRect rect = tab->rect.marginsRemoved(
3015 metrics.margins + metrics.padding + metrics.textPadding);
3016 if (!tab->icon.isNull())
3017 rect.setLeft(rect.left() + tab->iconSize.width() + metrics.spacing);
3018 return visualRect(tab->direction, tab->rect, rect);
3019 }
3020 break;
3021 case SE_TabBarTearIndicatorRight:
3022 case SE_TabBarTearIndicatorLeft:
3023 case SE_TabBarScrollLeftButton:
3024 case SE_TabBarScrollRightButton:
3025 // Not supported
3026 return QRect();
3027#endif // QT_CONFIG(tabbar)
3028#if QT_CONFIG(tabwidget)
3029 case SE_TabWidgetTabContents:
3030 if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
3031 // TabWidget corresponds to Page
3032 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Page, opt->state);
3033 if (!resolved.isValid())
3034 break;
3035 const QRect paneRect = subElementRect(SE_TabWidgetTabPane, opt, widget);
3036 const QRect rect = paneRect.marginsRemoved(resolved.metrics->padding);
3037 return visualRect(opt->direction, opt->rect, rect);
3038 }
3039 break;
3040#endif // QT_CONFIG(tabwidget)
3041#if QT_CONFIG(toolbar)
3042 case SE_ToolBarHandle:
3043 // Not supported
3044 return QRect();
3045#endif // QT_CONFIG(toolbar)
3046 default:
3047 break;
3048 }
3049 return QCommonStyle::subElementRect(r, opt, widget);
3050}
3051
3052/*! \reimp */
3053void QStyleKitStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
3054 QPainter *p, const QWidget *w) const
3055{
3056 Q_D(const QStyleKitStyle);
3057
3058 switch (cc) {
3059#if QT_CONFIG(slider)
3060 case CC_Slider:
3061 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3062 const auto r = d->resolve(w, QQStyleKitReader::ControlType::Slider, slider->state);
3063 if (!r.isValid())
3064 break;
3065 const auto &metrics = *r.metrics;
3066 QRect backgroundRect = opt->rect.marginsRemoved(metrics.margins);
3067
3068 // background
3069 d->drawStyledItemRect(r.background(), backgroundRect, p);
3070
3071 // groove
3072 if (slider->subControls & SC_SliderGroove) {
3073 // groove
3074 const auto grooveRect = proxy()->subControlRect(CC_Slider, opt, SC_SliderGroove, w);
3075 const auto *indicator = r.indicator();
3076 d->drawStyledItemRect(indicator, grooveRect, p);
3077
3078 // track
3079 const auto *foreground = indicator ? indicator->foreground() : nullptr;
3080 if (foreground && foreground->visible() && foreground->opacity() > 0) {
3081 const bool isHorizontal = slider->orientation == Qt::Horizontal;
3082 const qreal availableW = grooveRect.width()
3083 - foreground->leftMargin() - foreground->rightMargin();
3084 const qreal availableH = grooveRect.height()
3085 - foreground->topMargin() - foreground->bottomMargin();
3086
3087 const qreal range = slider->maximum - slider->minimum;
3088 const qreal ratio = range > 0
3089 ? (slider->sliderPosition - slider->minimum) / range
3090 : 0;
3091
3092 const auto hAlign = foreground->alignment() & Qt::AlignHorizontal_Mask;
3093 const auto vAlign = foreground->alignment() & Qt::AlignVertical_Mask;
3094 const qreal fgW = resolvedWidth(foreground, availableW);
3095 const qreal fgH = resolvedHeight(foreground, availableH);
3096 const qreal fgX = hAlign & Qt::AlignRight
3097 ? grooveRect.left() + foreground->leftMargin() + availableW - fgW
3098 : hAlign & Qt::AlignHCenter
3099 ? grooveRect.left() + foreground->leftMargin() + (availableW - fgW) / 2
3100 : grooveRect.left() + foreground->leftMargin();
3101 const qreal fgY = vAlign & Qt::AlignBottom
3102 ? grooveRect.top() + foreground->topMargin() + availableH - fgH
3103 : vAlign & Qt::AlignVCenter
3104 ? grooveRect.top() + foreground->topMargin() + (availableH - fgH) / 2
3105 : grooveRect.top() + foreground->topMargin();
3106
3107 const qreal minW = foreground->minimumWidth();
3108 QRectF trackRect;
3109 if (isHorizontal) {
3110 const qreal trackW = foreground->fillWidth()
3111 ? minW + ratio * (fgW - minW)
3112 : ratio * fgW;
3113 trackRect = QRectF(fgX, fgY, trackW, fgH);
3114 } else {
3115 const qreal trackH = foreground->fillHeight()
3116 ? minW + ratio * (fgH - minW)
3117 : ratio * fgH;
3118 const qreal trackY = fgY
3119 + (1.0 - ratio) * (foreground->fillHeight() ? fgH - minW : fgH);
3120 trackRect = QRectF(fgX, trackY, fgW, trackH);
3121 }
3122 // The track is the groove's foreground child, so it inherits the
3123 // indicator's transform.
3124 const QRect foregroundBox = visualRect(
3125 opt->direction, grooveRect, QRectF(fgX, fgY, fgW, fgH).toAlignedRect());
3126 QPainterStateGuard stateGuard(p);
3127 if (applyDelegateTransform(p, indicator, grooveRect)
3128 && applyDelegateTransform(p, foreground, foregroundBox)) {
3129 d->drawStyledItemContents(
3130 foreground,
3131 visualRect(opt->direction, grooveRect, trackRect.toAlignedRect()), p);
3132 }
3133 }
3134 }
3135
3136 // handle
3137 if (slider->subControls & SC_SliderHandle) {
3138 QStyleOptionSlider handleOpt(*slider);
3139 handleOpt.rect = subControlRect(CC_Slider, opt, SC_SliderHandle, w);
3140 const auto *handle = r.handle();
3141 if (handle && handle->visible() && handle->opacity() > 0)
3142 d->drawStyledItemRect(handle, handleOpt.rect, p);
3143 }
3144 return;
3145 }
3146 break;
3147#endif // QT_CONFIG(slider)
3148#if QT_CONFIG(combobox)
3149 case CC_ComboBox:
3150 if (const QStyleOptionComboBox *combo =
3151 qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
3152 // background
3153 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ComboBox, combo->state);
3154 if (!r.isValid())
3155 break;
3156 QRect frameRect = subControlRect(CC_ComboBox, opt, SC_ComboBoxFrame, w);
3157 d->drawStyledItemRect(r.background(), frameRect, p);
3158
3159 // indicator
3160 if (combo->subControls & SC_ComboBoxArrow) {
3161 QStyleOptionComboBox indicatorOpt(*combo);
3162 indicatorOpt.rect = subControlRect(CC_ComboBox, opt, SC_ComboBoxArrow, w);
3163 proxy()->drawPrimitive(PE_IndicatorArrowDown, &indicatorOpt, p, w);
3164 }
3165
3166 // The editable combobox paints its own line edit using the QPalette::Text role
3167 // for the text color, so update that color in the palette
3168 if (auto *cb = qobject_cast<const QComboBox *>(w); cb && cb->isEditable()) {
3169 if (const auto *txt = r.text(); txt && txt->isDefined(QQSK::Property::Color)) {
3170 QPalette stylePalette;
3171 stylePalette.setColor(QPalette::Text, txt->color());
3172 d->setStylePalette(cb->lineEdit(), stylePalette);
3173 }
3174 }
3175 return;
3176 }
3177 break;
3178#endif // QT_CONFIG(combobox)
3179#if QT_CONFIG(spinbox)
3180 case CC_SpinBox:
3181 if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
3182 const auto r = d->resolve(w, QQStyleKitReader::ControlType::SpinBox, spin->state);
3183 if (!r.isValid())
3184 break;
3185 QRect frameRect = opt->rect.marginsRemoved(r.metrics->margins);
3186 // background
3187 d->drawStyledItemRect(r.background(), frameRect, p);
3188 // up/down buttons
3189 if (spin->subControls & SC_SpinBoxUp) {
3190 QStyleOptionSpinBox upOpt(*spin);
3191 upOpt.rect = subControlRect(CC_SpinBox, opt, SC_SpinBoxUp, w);
3192 proxy()->drawPrimitive(PE_IndicatorSpinUp, &upOpt, p, w);
3193 }
3194 if (spin->subControls & SC_SpinBoxDown) {
3195 QStyleOptionSpinBox downOpt(*spin);
3196 downOpt.rect = subControlRect(CC_SpinBox, opt, SC_SpinBoxDown, w);
3197 proxy()->drawPrimitive(PE_IndicatorSpinDown, &downOpt, p, w);
3198 }
3199 // The spinbox line edit paints its text using the QPalette::Text role,
3200 // so update that color in the palette
3201 if (auto *sb = qobject_cast<const QSpinBox *>(w)) {
3202 if (const auto *txt = r.text(); txt && txt->isDefined(QQSK::Property::Color)) {
3203 QLineEdit *lineEdit = sb->findChild<QLineEdit *>();
3204 if (lineEdit) {
3205 QPalette p = lineEdit->palette();
3206 if (p.color(QPalette::Text) != txt->color()) {
3207 p.setColor(QPalette::Text, txt->color());
3208 lineEdit->setPalette(p);
3209 }
3210 }
3211 }
3212 }
3213 return;
3214 }
3215 break;
3216#endif // QT_CONFIG(spinbox)
3217#if QT_CONFIG(groupbox)
3218 case CC_GroupBox:
3219 if (const QStyleOptionGroupBox *groupBox =
3220 qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
3221 const auto r = d->resolve(w, QQStyleKitReader::ControlType::GroupBox, groupBox->state);
3222 if (!r.isValid())
3223 break;
3224
3225 // background/frame
3226 if (groupBox->subControls & SC_GroupBoxFrame) {
3227 QStyleOptionFrame frameOpt;
3228 frameOpt.QStyleOption::operator=(*groupBox);
3229 frameOpt.rect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, w);
3230 drawPrimitive(PE_FrameGroupBox, &frameOpt, p, w);
3231 }
3232
3233 // title
3234 if (groupBox->subControls & SC_GroupBoxLabel && !groupBox->text.isEmpty()) {
3235 const auto *textProps = r.text();
3236 const QFont textFont = r.font();
3237 QRect titleRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, w);
3238 d->drawControlText(textProps, textFont,
3239 titleRect.marginsRemoved(r.metrics->textPadding), groupBox->text,
3240 Qt::TextShowMnemonic, p, Qt::AlignLeft | Qt::AlignVCenter);
3241 }
3242 // don't draw checkmark as the Controls style doesn't draw it
3243 // and StyleKit doesn't provide a way to style it
3244 return;
3245 }
3246 break;
3247#endif // QT_CONFIG(groupbox)
3248#if QT_CONFIG(scrollbar)
3249 case CC_ScrollBar:
3250 if (const auto *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3251 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ScrollBar,
3252 scrollbar->state);
3253 if (!r.isValid())
3254 break;
3255
3256 // background = groove
3257 if (scrollbar->subControls & SC_ScrollBarGroove) {
3258 QRect backgroundRect = proxy()->subControlRect(
3259 CC_ScrollBar, opt, SC_ScrollBarGroove, w);
3260 d->drawStyledItemRect(r.background(), backgroundRect, p);
3261 }
3262
3263 // TODO: increase button
3264 // TODO: decrease button
3265
3266 // slider = indicator
3267 if (scrollbar->subControls & SC_ScrollBarSlider) {
3268 QStyleOptionSlider newScrollbar(*scrollbar);
3269 newScrollbar.rect = proxy()->subControlRect(
3270 CC_ScrollBar, opt, SC_ScrollBarSlider, w);
3271 proxy()->drawControl(CE_ScrollBarSlider, &newScrollbar, p, w);
3272 }
3273 return;
3274 }
3275 break;
3276#endif // QT_CONFIG(scrollbar)
3277#if QT_CONFIG(toolbutton)
3278 case CC_ToolButton:
3279 if (const QStyleOptionToolButton *tool =
3280 qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
3281 const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolButton, tool->state);
3282 if (!r.isValid())
3283 break;
3284
3285 // background
3286 d->drawStyledItemRect(r.background(), opt->rect, p);
3287
3288 // menu arrow indicator
3289 if ((tool->features
3290 & (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::PopupDelay))
3291 == QStyleOptionToolButton::MenuButtonPopup
3292 && (tool->subControls & SC_ToolButtonMenu)) {
3293 const QRect menuRect = subControlRect(CC_ToolButton, opt, SC_ToolButtonMenu, w);
3294 d->drawControlIndicator(r.indicator(), menuRect, p);
3295 }
3296
3297 // label
3298 State bflags = tool->state & ~State_Sunken;
3299 if (bflags & State_AutoRaise) {
3300 if (!(bflags & State_MouseOver) || !(bflags & State_Enabled))
3301 bflags &= ~State_Raised;
3302 }
3303 if (tool->state & State_Sunken && tool->activeSubControls & SC_ToolButton)
3304 bflags |= State_Sunken;
3305 QStyleOptionToolButton label = *tool;
3306 label.state = bflags;
3307 label.rect = subControlRect(CC_ToolButton, opt, SC_ToolButton, w);
3308 proxy()->drawControl(CE_ToolButtonLabel, &label, p, w);
3309 return;
3310 }
3311 break;
3312#endif // QT_CONFIG(toolbutton)
3313 default:
3314 break;
3315 }
3316 QCommonStyle::drawComplexControl(cc, opt, p, w);
3317}
3318
3319/*! \reimp */
3320QStyle::SubControl QStyleKitStyle::hitTestComplexControl(ComplexControl cc,
3321 const QStyleOptionComplex *opt,
3322 const QPoint &pt, const QWidget *w) const
3323{
3324 return QCommonStyle::hitTestComplexControl(cc, opt, pt, w);
3325}
3326
3327/*! \reimp */
3328QRect QStyleKitStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
3329 SubControl sc, const QWidget *w) const
3330{
3331 using QtPrivate::qSaturateRound;
3332 Q_D(const QStyleKitStyle);
3333
3334 switch (cc) {
3335#if QT_CONFIG(slider)
3336 case CC_Slider:
3337 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3338 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Slider,
3339 opt->state);
3340 if (!resolved.isValid())
3341 break;
3342 const auto &metrics = *resolved.metrics;
3343 const bool horizontal = slider->orientation == Qt::Horizontal;
3344 QRect contentsRect = opt->rect
3345 .marginsRemoved(metrics.margins)
3346 .marginsRemoved(metrics.padding);
3347
3348 const auto *indicator = resolved.indicator();
3349 if (!indicator)
3350 return contentsRect;
3351 switch (sc) {
3352 case SC_SliderGroove: {
3353 const auto availableW = contentsRect.width()
3354 - indicator->leftMargin() - indicator->rightMargin();
3355 const auto availableH = contentsRect.height()
3356 - indicator->topMargin() - indicator->bottomMargin();
3357 const qreal grooveW = resolvedWidth(indicator, availableW);
3358 const qreal grooveH = resolvedHeight(indicator, availableH);
3359
3360 QRectF grooveRect(0, 0, grooveW, grooveH);
3361 const uint rawHAlign = indicator->alignment() & Qt::AlignHorizontal_Mask;
3362 const uint rawVAlign = indicator->alignment() & Qt::AlignVertical_Mask;
3363 const uint hAlign = rawHAlign
3364 ? static_cast<Qt::Alignment>(rawHAlign)
3365 : Qt::AlignLeft;
3366 const uint vAlign = rawVAlign
3367 ? static_cast<Qt::Alignment>(rawVAlign)
3368 : Qt::AlignVCenter;
3369
3370 if (hAlign & Qt::AlignLeft) {
3371 grooveRect.moveLeft(contentsRect.x() + indicator->leftMargin());
3372 } else if (hAlign & Qt::AlignHCenter) {
3373 const qreal availableWidth = contentsRect.width()
3374 - indicator->leftMargin() - indicator->rightMargin();
3375 grooveRect.moveLeft(contentsRect.x() + indicator->leftMargin()
3376 + (availableWidth - grooveW) / 2.0);
3377 } else {
3378 grooveRect.moveLeft(contentsRect.x() + contentsRect.width()
3379 - indicator->rightMargin() - grooveW);
3380 }
3381
3382 if (vAlign & Qt::AlignTop) {
3383 grooveRect.moveTop(contentsRect.y() + indicator->topMargin());
3384 } else if (vAlign & Qt::AlignVCenter) {
3385 const qreal availableHeight = contentsRect.height()
3386 - indicator->topMargin() - indicator->bottomMargin();
3387 grooveRect.moveTop(contentsRect.y()
3388 + indicator->topMargin()
3389 + (availableHeight - grooveH) / 2.0);
3390 } else {
3391 grooveRect.moveTop(contentsRect.y()
3392 + contentsRect.height()
3393 - indicator->bottomMargin() - grooveH);
3394 }
3395 return visualRect(opt->direction, opt->rect, grooveRect.toAlignedRect());
3396 }
3397 case SC_SliderHandle: {
3398 const auto *handle = resolved.handle();
3399 if (!handle || !handle->visible() || handle->opacity() == 0)
3400 return contentsRect;
3401
3402 const qreal handleW = resolvedWidth(
3403 handle, contentsRect.width() - handle->leftMargin() - handle->rightMargin());
3404 const qreal handleH = resolvedHeight(
3405 handle, contentsRect.height() - handle->topMargin() - handle->bottomMargin());
3406 QRectF handleRect(0, 0, handleW, handleH);
3407 if (horizontal) {
3408 const int range = qSaturateRound(contentsRect.width()
3409 - handle->leftMargin() - handle->rightMargin() - handleW);
3410 const int sliderPos = QStyle::sliderPositionFromValue(
3411 slider->minimum, slider->maximum, slider->sliderPosition, range, false);
3412 handleRect.moveLeft(contentsRect.x() + handle->leftMargin() + sliderPos);
3413 handleRect.moveTop(contentsRect.y()
3414 + handle->topMargin() - handle->bottomMargin()
3415 + (contentsRect.height() - handleH) / 2.0);
3416 } else {
3417 const int range = qSaturateRound(contentsRect.height()
3418 - handle->topMargin() - handle->bottomMargin() - handleH);
3419 const int sliderPos = QStyle::sliderPositionFromValue(
3420 slider->minimum, slider->maximum, slider->sliderPosition, range, true);
3421 handleRect.moveLeft(contentsRect.x()
3422 + handle->leftMargin() - handle->rightMargin()
3423 + (contentsRect.width() - handleW) / 2.0);
3424 handleRect.moveTop(contentsRect.y() + handle->topMargin() + sliderPos);
3425 }
3426 return visualRect(opt->direction, opt->rect, handleRect.toAlignedRect());
3427 }
3428 default:
3429 break;
3430 }
3431 }
3432 break;
3433#endif // QT_CONFIG(slider)
3434#if QT_CONFIG(combobox)
3435 case CC_ComboBox:
3436 if (const QStyleOptionComboBox *combo =
3437 qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
3438 const auto r = d->resolveLayout(QQStyleKitReader::ControlType::ComboBox, combo->state);
3439 if (!r.isValid())
3440 break;
3441 const auto &metrics = *r.metrics;
3442 QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
3443 switch (sc) {
3444 case SC_ComboBoxFrame:
3445 return visualRect(opt->direction, opt->rect, frameRect);
3446 case SC_ComboBoxEditField: {
3447 QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
3448 QRect indicatorRect = subControlRect(CC_ComboBox, opt, SC_ComboBoxArrow, w);
3449 const int spacing = metrics.spacing;
3450 const auto *indicator = r.indicator();
3451 const uint indicatorAlign = indicator
3452 ? resolvedAlignment(indicator->alignment(), Qt::AlignRight, Qt::AlignVCenter)
3453 : uint(Qt::AlignRight | Qt::AlignVCenter);
3454 if (indicatorAlign & Qt::AlignLeft) {
3455 contentsRect.setLeft(indicatorRect.right() + spacing);
3456 } else if (indicatorAlign & Qt::AlignRight) {
3457 contentsRect.setRight(indicatorRect.left() - spacing);
3458 }
3459 return visualRect(opt->direction, opt->rect, contentsRect);
3460 }
3461 case SC_ComboBoxArrow: {
3462 QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
3463 const auto *indicator = r.indicator();
3464 if (!indicator || !indicator->visible() || indicator->opacity() == 0)
3465 return QRect();
3466
3467 const int w = qSaturateRound(resolvedWidth(
3468 indicator,
3469 contentsRect.width() - indicator->leftMargin() - indicator->rightMargin()));
3470 const int h = qSaturateRound(resolvedHeight(
3471 indicator,
3472 contentsRect.height() - indicator->topMargin() - indicator->bottomMargin()));
3473 const uint indicatorAlign = resolvedAlignment(
3474 indicator->alignment(), Qt::AlignRight, Qt::AlignVCenter);
3475 const QMargins indicatorMargins = elementMargins(indicator);
3476 return visualRect(
3477 opt->direction, opt->rect,
3478 d->getAlignedRectInContainer(contentsRect, QSize(w, h),
3479 indicatorAlign, QMargins(),
3480 indicatorMargins));
3481 }
3482 case SC_ComboBoxListBoxPopup: {
3483 QRect popupRect = opt->rect;
3484 popupRect.setTop(opt->rect.bottom());
3485 return visualRect(opt->direction, opt->rect, popupRect);
3486 }
3487 default:
3488 break;
3489 }
3490 }
3491 break;
3492#endif // QT_CONFIG(combobox)
3493#if QT_CONFIG(spinbox)
3494 case CC_SpinBox:
3495 if (const QStyleOptionSpinBox *spinBox =
3496 qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
3497 const auto r = d->resolveLayout(QQStyleKitReader::ControlType::SpinBox, spinBox->state);
3498 if (!r.isValid())
3499 break;
3500 const auto &metrics = *r.metrics;
3501 QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
3502 QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
3503 switch (sc) {
3504 case SC_SpinBoxFrame:
3505 return visualRect(opt->direction, opt->rect, frameRect);
3506 case SC_SpinBoxEditField: {
3507 QRect upIndicatorRect = subControlRect(CC_SpinBox, opt, SC_SpinBoxUp, w);
3508 QRect downIndicatorRect = subControlRect(CC_SpinBox, opt, SC_SpinBoxDown, w);
3509 const int spacing = metrics.spacing;
3510 const auto *upIndicator = r.indicator() ? r.indicator()->first() : nullptr;
3511 const auto *downIndicator = r.indicator() ? r.indicator()->second() : nullptr;
3512 const bool hasUpIndicator = upIndicator
3513 && upIndicator->visible() && upIndicator->opacity() > 0;
3514 const bool hasDownIndicator = downIndicator
3515 && downIndicator->visible() && downIndicator->opacity() > 0;
3516 const uint upAlign = hasUpIndicator
3517 ? resolvedAlignment(upIndicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
3518 : uint(Qt::AlignLeft | Qt::AlignVCenter);
3519 const uint downAlign = hasDownIndicator
3520 ? resolvedAlignment(downIndicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
3521 : uint(Qt::AlignLeft | Qt::AlignVCenter);
3522 if (hasUpIndicator && hasDownIndicator) {
3523 if ((upAlign & Qt::AlignLeft) && (downAlign & Qt::AlignRight)) {
3524 contentsRect.setLeft(upIndicatorRect.right() + spacing);
3525 contentsRect.setRight(downIndicatorRect.left() - spacing);
3526 } else if ((upAlign & Qt::AlignRight) && (downAlign & Qt::AlignLeft)) {
3527 contentsRect.setRight(upIndicatorRect.left() - spacing);
3528 contentsRect.setLeft(downIndicatorRect.right() + spacing);
3529 }
3530 } else if (hasUpIndicator) {
3531 if (upAlign & Qt::AlignLeft) {
3532 contentsRect.setLeft(upIndicatorRect.right() + spacing);
3533 } else if (upAlign & Qt::AlignRight) {
3534 contentsRect.setRight(upIndicatorRect.left() - spacing);
3535 }
3536 } else if (hasDownIndicator) {
3537 if (downAlign & Qt::AlignLeft) {
3538 contentsRect.setLeft(downIndicatorRect.right() + spacing);
3539 } else if (downAlign & Qt::AlignRight) {
3540 contentsRect.setRight(downIndicatorRect.left() - spacing);
3541 }
3542 }
3543 return visualRect(opt->direction, opt->rect, contentsRect);
3544 }
3545 case SC_SpinBoxUp: {
3546 const auto *upIndicator = r.indicator() ? r.indicator()->first() : nullptr;
3547 if (!upIndicator || !upIndicator->visible() || upIndicator->opacity() == 0)
3548 return contentsRect;
3549
3550 const int w = qSaturateRound(resolvedWidth(
3551 upIndicator,
3552 contentsRect.width() - upIndicator->leftMargin() - upIndicator->rightMargin()));
3553 const int h = qSaturateRound(resolvedHeight(
3554 upIndicator,
3555 contentsRect.height() - upIndicator->topMargin() - upIndicator->bottomMargin()));
3556 const uint upAlign = resolvedAlignment(
3557 upIndicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter);
3558 const QMargins upMargins = elementMargins(upIndicator);
3559 return visualRect(opt->direction, opt->rect,
3560 d->getAlignedRectInContainer(contentsRect, QSize(w, h), upAlign,
3561 QMargins(), upMargins));
3562 }
3563 case SC_SpinBoxDown: {
3564 const auto *downIndicator = r.indicator() ? r.indicator()->second() : nullptr;
3565 if (!downIndicator || !downIndicator->visible() || downIndicator->opacity() == 0)
3566 return frameRect;
3567
3568 const int w = qSaturateRound(resolvedWidth(
3569 downIndicator,
3570 contentsRect.width()
3571 - downIndicator->leftMargin() - downIndicator->rightMargin()));
3572 const int h = qSaturateRound(resolvedHeight(
3573 downIndicator,
3574 contentsRect.height()
3575 - downIndicator->topMargin() - downIndicator->bottomMargin()));
3576 const uint downAlign = resolvedAlignment(downIndicator->alignment(), Qt::AlignLeft,
3577 Qt::AlignVCenter);
3578 const QMargins downMargins = elementMargins(downIndicator);
3579 return visualRect(opt->direction, opt->rect,
3580 d->getAlignedRectInContainer(contentsRect, QSize(w, h), downAlign,
3581 QMargins(), downMargins));
3582 }
3583 default:
3584 break;
3585 }
3586 }
3587 break;
3588#endif // QT_CONFIG(spinbox)
3589#if QT_CONFIG(groupbox)
3590 case CC_GroupBox:
3591 if (const QStyleOptionGroupBox *groupBox =
3592 qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
3593 const auto r = d->resolveLayout(QQStyleKitReader::ControlType::GroupBox,
3594 groupBox->state);
3595 if (!r.isValid())
3596 break;
3597 const auto &metrics = *r.metrics;
3598 QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
3599 switch (sc) {
3600 case SC_GroupBoxFrame:
3601 return visualRect(opt->direction, opt->rect, frameRect);
3602 case SC_GroupBoxLabel: {
3603 const int fontHeight = opt->fontMetrics.height();
3604 const int labelHeight = metrics.textPadding.top()
3605 + fontHeight + metrics.textPadding.bottom();
3606 const QRect labelContainer(opt->rect.left(), opt->rect.top(), opt->rect.width(),
3607 labelHeight);
3608 return visualRect(opt->direction, opt->rect, labelContainer);
3609 }
3610 case SC_GroupBoxCheckBox: {
3611 // The controls style doesn't draw the checkbox, so we don't return a rect for it
3612 return QRect();
3613 }
3614 case SC_GroupBoxContents: {
3615 const auto labelHeight = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, w)
3616 .height();
3617 const auto offset = metrics.padding.top()
3618 + qMax(labelHeight, metrics.margins.top() + metrics.spacing);
3619 QRect contentsRect(
3620 opt->rect.left() + metrics.padding.left(), opt->rect.top() + offset,
3621 opt->rect.width() - metrics.padding.left() - metrics.padding.right(),
3622 opt->rect.bottom() - opt->rect.top() - offset - metrics.padding.bottom());
3623 return visualRect(opt->direction, opt->rect, contentsRect);
3624 }
3625 default:
3626 break;
3627 }
3628 }
3629 break;
3630#endif // QT_CONFIG(groupbox)
3631#if QT_CONFIG(scrollbar)
3632 case CC_ScrollBar:
3633 if (const auto *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3634 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ScrollBar,
3635 opt->state);
3636 if (!resolved.isValid())
3637 break;
3638 const auto &metrics = *resolved.metrics;
3639 const bool horizontal = scrollbar->orientation == Qt::Horizontal;
3640 const QRectF grooveRect = opt->rect.marginsRemoved(metrics.margins);
3641
3642 switch (sc) {
3643 // TODO: support up/down buttons
3644 case SC_ScrollBarAddLine:
3645 case SC_ScrollBarSubLine:
3646 case SC_ScrollBarSubPage:
3647 case SC_ScrollBarAddPage:
3648 return QRect();
3649 case SC_ScrollBarGroove:
3650 return visualRect(opt->direction, opt->rect, grooveRect.toAlignedRect());
3651 case SC_ScrollBarSlider: {
3652 const auto *indicator = resolved.indicator();
3653 if (!indicator || !indicator->visible() || indicator->opacity() == 0)
3654 return QRect();
3655 const QRectF contentsRect = grooveRect.marginsRemoved(metrics.padding);
3656
3657 const qreal availableW = contentsRect.width()
3658 - indicator->leftMargin() - indicator->rightMargin();
3659 const qreal availableH = contentsRect.height()
3660 - indicator->topMargin() - indicator->bottomMargin();
3661
3662 const int totalRange = scrollbar->maximum
3663 - scrollbar->minimum + scrollbar->pageStep;
3664 const qreal ratio = totalRange > 0 ? qreal(scrollbar->pageStep) / totalRange : 1.0;
3665
3666 QRectF sliderRect;
3667 if (horizontal) {
3668 const qreal sliderW = qMax(
3669 qreal(metrics.indicatorImplicitSize.width()), availableW * ratio);
3670 const qreal sliderH = resolvedHeight(indicator, availableH);
3671 const qreal travelRange = qMax(0.0, availableW - sliderW);
3672 const int sliderPos = QStyle::sliderPositionFromValue(
3673 scrollbar->minimum, scrollbar->maximum, scrollbar->sliderPosition,
3674 int(travelRange), scrollbar->upsideDown);
3675 sliderRect = QRectF(
3676 contentsRect.x() + indicator->leftMargin() + sliderPos,
3677 contentsRect.y() + indicator->topMargin() + (availableH - sliderH) / 2.0,
3678 sliderW, sliderH);
3679 } else {
3680 const qreal sliderH = qMax(
3681 qreal(metrics.indicatorImplicitSize.width()), availableH * ratio);
3682 const qreal sliderW = resolvedWidth(indicator, availableW);
3683 const qreal travelRange = qMax(0.0, availableH - sliderH);
3684 const int sliderPos = QStyle::sliderPositionFromValue(
3685 scrollbar->minimum, scrollbar->maximum, scrollbar->sliderPosition,
3686 int(travelRange), scrollbar->upsideDown);
3687 sliderRect = QRectF(
3688 contentsRect.x() + indicator->leftMargin() + (availableW - sliderW) / 2.0,
3689 contentsRect.y() + indicator->topMargin() + sliderPos, sliderW, sliderH);
3690 }
3691 return visualRect(opt->direction, opt->rect, sliderRect.toAlignedRect());
3692 }
3693 default:
3694 break;
3695 }
3696 }
3697 break;
3698#endif // QT_CONFIG(scrollbar)
3699#if QT_CONFIG(toolbutton)
3700 case CC_ToolButton:
3701 if (const auto *tool = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
3702 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolButton,
3703 opt->state);
3704 if (!resolved.isValid())
3705 break;
3706 const auto &metrics = *resolved.metrics;
3707 QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
3708 switch (sc) {
3709 case SC_ToolButton: {
3710 QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
3711 const bool hasMenuButton = (tool->features
3712 & (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::HasMenu))
3713 != 0;
3714 const auto *indicator = resolved.indicator();
3715 if (hasMenuButton && indicator && indicator->visible()
3716 && indicator->opacity() > 0) {
3717 const QRect menuRect = subControlRect(CC_ToolButton, opt, SC_ToolButtonMenu, w);
3718 const int spacing = metrics.spacing;
3719 const uint indicatorAlign = resolvedAlignment(indicator->alignment(),
3720 Qt::AlignRight, Qt::AlignVCenter);
3721 if (indicatorAlign & Qt::AlignLeft)
3722 contentsRect.setLeft(menuRect.right() + spacing);
3723 else if (indicatorAlign & Qt::AlignRight)
3724 contentsRect.setRight(menuRect.left() - spacing);
3725 }
3726 return visualRect(opt->direction, opt->rect, contentsRect);
3727 }
3728 case SC_ToolButtonMenu: {
3729 QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
3730 if ((tool->features
3731 & (QStyleOptionToolButton::MenuButtonPopup
3732 | QStyleOptionToolButton::PopupDelay))
3733 != QStyleOptionToolButton::MenuButtonPopup) {
3734 break;
3735 }
3736
3737 const auto *indicator = resolved.indicator();
3738 if (!indicator || !indicator->visible() || indicator->opacity() == 0)
3739 return QRect();
3740
3741 const int w = qSaturateRound(resolvedWidth(
3742 indicator,
3743 contentsRect.width() - indicator->leftMargin() - indicator->rightMargin()));
3744 const int h = qSaturateRound(resolvedHeight(
3745 indicator,
3746 contentsRect.height() - indicator->topMargin() - indicator->bottomMargin()));
3747 const uint indicatorAlign = resolvedAlignment(indicator->alignment(),
3748 Qt::AlignRight, Qt::AlignVCenter);
3749 const QMargins indicatorMargins = elementMargins(indicator);
3750 return visualRect(opt->direction, opt->rect,
3751 d->getAlignedRectInContainer(contentsRect, QSize(w, h), indicatorAlign, QMargins(), indicatorMargins));
3752 }
3753 default:
3754 break;
3755 }
3756 }
3757 break;
3758#endif // QT_CONFIG(toolbutton)
3759 default:
3760 break;
3761 }
3762 return QCommonStyle::subControlRect(cc, opt, sc, w);
3763}
3764
3765/*! \reimp */
3766QSize QStyleKitStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
3767 const QSize &contentsSize, const QWidget *widget) const
3768{
3769 Q_D(const QStyleKitStyle);
3770
3771 switch (ct) {
3772#if QT_CONFIG(pushbutton)
3773 case CT_PushButton:
3774#endif
3775#if QT_CONFIG(toolbutton)
3776 case CT_ToolButton: {
3777 auto controlType = QQStyleKitReader::ControlType::ToolButton;
3778 if (const auto *buttonOpt = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3779 controlType = (buttonOpt->features & QStyleOptionButton::Flat)
3780 ? QQStyleKitReader::ControlType::FlatButton
3781 : QQStyleKitReader::ControlType::Button;
3782 }
3783 const auto resolved = d->resolveLayout(controlType, opt->state);
3784 if (!resolved.isValid())
3785 break;
3786
3787 const auto &metrics = *resolved.metrics;
3788 const QSize contentSize = contentsSize
3789 + QSize(metrics.padding.left() + metrics.padding.right(),
3790 metrics.padding.top() + metrics.padding.bottom())
3791 + QSize(metrics.textPadding.left() + metrics.textPadding.right(),
3792 metrics.textPadding.top() + metrics.textPadding.bottom());
3793 const QSize bgSize = metrics.bgImplicitSize
3794 + QSize(metrics.margins.left() + metrics.margins.right(),
3795 metrics.margins.top() + metrics.margins.bottom());
3796 return contentSize.expandedTo(bgSize);
3797 }
3798#endif
3799 case CT_CheckBox:
3800 case CT_RadioButton:
3801 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3802 const auto controlType = ct == CT_CheckBox ? QQStyleKitReader::ControlType::CheckBox
3803 : QQStyleKitReader::ControlType::RadioButton;
3804 const auto resolved = d->resolveLayout(controlType, opt->state);
3805 if (!resolved.isValid())
3806 break;
3807 const auto &metrics = *resolved.metrics;
3808 const QSize textSize = opt->fontMetrics.size(Qt::TextShowMnemonic, btn->text);
3809 const int bgWidth = metrics.bgImplicitSize.width()
3810 + metrics.margins.left() + metrics.margins.right();
3811 const int bgHeight = metrics.bgImplicitSize.height()
3812 + metrics.margins.top() + metrics.margins.bottom();
3813 const int contentWidth = std::max(textSize.width(), contentsSize.width())
3814 + metrics.padding.left() + metrics.padding.right() + metrics.textPadding.left()
3815 + metrics.textPadding.right();
3816 const int contentHeight = std::max(textSize.height(), contentsSize.height())
3817 + metrics.padding.top() + metrics.padding.bottom() + metrics.textPadding.top()
3818 + metrics.textPadding.bottom();
3819 const int indicatorWidth = metrics.indicatorImplicitSize.width()
3820 + metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
3821 const int indicatorHeight = metrics.indicatorImplicitSize.height()
3822 + metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
3823 return QSize(std::max({ contentWidth + indicatorWidth + metrics.spacing, bgWidth }),
3824 std::max({ contentHeight, indicatorHeight, bgHeight }));
3825 }
3826 break;
3827#if QT_CONFIG(itemviews)
3828 case CT_ItemViewItem:
3829 if (const auto *item = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
3830 const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
3831 if (!resolved.isValid())
3832 break;
3833 const auto &metrics = *resolved.metrics;
3834 const QSize textSize = opt->fontMetrics.size(Qt::TextShowMnemonic, item->text);
3835 const int bgWidth = metrics.bgImplicitSize.width()
3836 + metrics.margins.left() + metrics.margins.right();
3837 const int bgHeight = metrics.bgImplicitSize.height()
3838 + metrics.margins.top() + metrics.margins.bottom();
3839 const int contentWidth = std::max(textSize.width(), contentsSize.width())
3840 + metrics.padding.left() + metrics.padding.right() + metrics.textPadding.left()
3841 + metrics.textPadding.right();
3842 const int contentHeight = std::max(textSize.height(), contentsSize.height())
3843 + metrics.padding.top() + metrics.padding.bottom() + metrics.textPadding.top()
3844 + metrics.textPadding.bottom();
3845 const int indicatorWidth = metrics.indicatorImplicitSize.width()
3846 + metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
3847 const int indicatorHeight = metrics.indicatorImplicitSize.height()
3848 + metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
3849 return QSize(std::max({ contentWidth + indicatorWidth + metrics.spacing, bgWidth }),
3850 std::max({ contentHeight, indicatorHeight, bgHeight }));
3851 }
3852 break;
3853#endif // QT_CONFIG(itemviews)
3854 case CT_ProgressBar:
3855 if (qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
3856 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ProgressBar,
3857 opt->state);
3858 if (!resolved.isValid())
3859 break;
3860 const auto &metrics = *resolved.metrics;
3861 const auto indicatorW = std::max(
3862 metrics.indicatorImplicitSize.width()
3863 + metrics.indicatorMargins.left()
3864 + metrics.indicatorMargins.right(),
3865 metrics.foregroundImplicitSize.width()
3866 + metrics.foregroundMargins.left()
3867 + metrics.foregroundMargins.right());
3868 const auto indicatorH = std::max(
3869 metrics.indicatorImplicitSize.height()
3870 + metrics.indicatorMargins.top()
3871 + metrics.indicatorMargins.bottom(),
3872 metrics.foregroundImplicitSize.height()
3873 + metrics.foregroundMargins.top()
3874 + metrics.foregroundMargins.bottom());
3875 const int bgW = metrics.bgImplicitSize.width()
3876 + metrics.margins.left() + metrics.margins.right();
3877 const int bgH = metrics.bgImplicitSize.height()
3878 + metrics.margins.top() + metrics.margins.bottom();
3879 // For progress bar in Controls, the content size is based on the indicator size
3880 const int contentW = indicatorW + metrics.padding.left() + metrics.padding.right();
3881 const int contentH = indicatorH + metrics.padding.top() + metrics.padding.bottom();
3882 return QSize(std::max(contentW, bgW), std::max(contentH, bgH));
3883 }
3884 break;
3885 case CT_Slider:
3886 if (qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3887 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Slider,
3888 opt->state);
3889 if (!resolved.isValid())
3890 break;
3891 const auto &metrics = *resolved.metrics;
3892 // background = indicator = groove + track
3893 const int bgW = std::max(
3894 metrics.padding.left() + metrics.padding.right()
3895 + metrics.indicatorImplicitSize.width()
3896 + metrics.indicatorMargins.left() + metrics.indicatorMargins.right(),
3897 metrics.bgImplicitSize.width() + metrics.margins.left() + metrics.margins.right());
3898 const int bgH = std::max(
3899 metrics.padding.top() + metrics.padding.bottom()
3900 + metrics.indicatorImplicitSize.height()
3901 + metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom(),
3902 metrics.bgImplicitSize.height() + metrics.margins.top() + metrics.margins.bottom());
3903 const int handleW = metrics.handleImplicitSize.width()
3904 + metrics.padding.left()
3905 + metrics.padding.right();
3906 const int handleH = metrics.handleImplicitSize.height()
3907 + metrics.padding.top()
3908 + metrics.padding.bottom();
3909 return QSize(std::max(handleW, bgW), std::max(handleH, bgH));
3910 }
3911 break;
3912#if QT_CONFIG(lineedit)
3913 case CT_LineEdit:
3914 if (const auto *lineEdit = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
3915 QStyleOption lineEditOpt(*lineEdit);
3916 lineEditOpt.state &= ~QStyle::State_Sunken;
3917# if QT_CONFIG(spinbox)
3918 const bool isInSpinBox = widget && qobject_cast<const QSpinBox *>(widget->parent());
3919# else
3920 const bool isInSpinBox = false;
3921# endif
3922 auto controlType = isInSpinBox ? QQStyleKitReader::ControlType::SpinBox
3923 : QQStyleKitReader::ControlType::TextField;
3924 const auto resolved = d->resolveLayout(controlType, lineEditOpt.state);
3925 if (!resolved.isValid())
3926 break;
3927 const auto &metrics = *resolved.metrics;
3928 QSize bgSize(0, 0);
3929 // For spinbox, the line edit doesn't have its own background in the Controls style
3930 if (!isInSpinBox)
3931 bgSize = metrics.bgImplicitSize.grownBy(metrics.margins);
3932 const QSize contentSizeWithPadding = contentsSize
3933 .grownBy(metrics.textPadding)
3934 .grownBy(metrics.padding);
3935 return contentSizeWithPadding.expandedTo(bgSize);
3936 }
3937 break;
3938#endif // QT_CONFIG(lineedit)
3939#if QT_CONFIG(combobox)
3940 case CT_ComboBox:
3941 if (const auto *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
3942 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ComboBox,
3943 opt->state);
3944 if (!resolved.isValid())
3945 break;
3946 const auto &metrics = *resolved.metrics;
3947 const QSize textSize = opt->fontMetrics.size(
3948 Qt::TextShowMnemonic, comboBox->currentText);
3949 const int bgW = metrics.bgImplicitSize.width()
3950 + metrics.margins.left() + metrics.margins.right();
3951 const int bgH = metrics.bgImplicitSize.height()
3952 + metrics.margins.top() + metrics.margins.bottom();
3953 const int contentW = textSize.width()
3954 + metrics.padding.left() + metrics.padding.right()
3955 + metrics.textPadding.left() + metrics.textPadding.right();
3956 const int contentH = textSize.height()
3957 + metrics.padding.top() + metrics.padding.bottom()
3958 + metrics.textPadding.top() + metrics.textPadding.bottom();
3959 const int indicatorW = metrics.indicatorImplicitSize.width()
3960 + metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
3961 const int indicatorH = metrics.indicatorImplicitSize.height()
3962 + metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
3963 return QSize(std::max({ contentW + indicatorW + metrics.spacing, bgW }),
3964 std::max({ contentH, indicatorH, bgH }));
3965 }
3966 break;
3967#endif // QT_CONFIG(combobox)
3968#if QT_CONFIG(spinbox)
3969 case CT_SpinBox: {
3970 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::SpinBox, opt->state);
3971 if (!resolved.isValid())
3972 break;
3973 const auto &metrics = *resolved.metrics;
3974 const int bgW = metrics.bgImplicitSize.width()
3975 + metrics.margins.left() + metrics.margins.right();
3976 const int bgH = metrics.bgImplicitSize.height()
3977 + metrics.margins.top() + metrics.margins.bottom();
3978 const int contentW = contentsSize.width()
3979 + metrics.padding.left() + metrics.padding.right()
3980 + metrics.textPadding.left() + metrics.textPadding.right();
3981 const int contentH = contentsSize.height()
3982 + metrics.padding.top() + metrics.padding.bottom()
3983 + metrics.textPadding.top() + metrics.textPadding.bottom();
3984 // TODO: Support vertical layout for spinbox, currently we assume horizontal layout
3985 // TODO: Calculate each indicator (up/down) size separately if they have different sizes in
3986 // the style
3987 const int indicatorW = (metrics.indicatorImplicitSize.width()
3988 + metrics.indicatorMargins.left() + metrics.indicatorMargins.right()) * 2;
3989 const int indicatorH = metrics.indicatorImplicitSize.height()
3990 + metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
3991 return QSize(std::max({ contentW + indicatorW + metrics.spacing, bgW }),
3992 std::max({ contentH, indicatorH, bgH }));
3993 }
3994#endif // QT_CONFIG(spinbox)
3995#if QT_CONFIG(tabbar)
3996 case CT_TabBarTab:
3997 if (qstyleoption_cast<const QStyleOptionTab *>(opt)) {
3998 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::TabButton,
3999 opt->state);
4000 if (!resolved.isValid())
4001 break;
4002 const auto &m = *resolved.metrics;
4003 const auto bgSize = m.bgImplicitSize.grownBy(m.margins);
4004 const auto contentSizeWithPadding = contentsSize.grownBy(m.padding + m.textPadding);
4005 return contentSizeWithPadding.expandedTo(bgSize);
4006 }
4007 break;
4008#endif // QT_CONFIG(tabbar)
4009#if QT_CONFIG(tabwidget)
4010 case CT_TabWidget:
4011 if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
4012 // TabWidget corresponds to Page
4013 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Page, opt->state);
4014 if (!resolved.isValid())
4015 break;
4016 const auto &m = *resolved.metrics;
4017 const auto bgSize = m.bgImplicitSize.grownBy(m.margins);
4018 const auto contentSizeWithPadding = contentsSize.grownBy(m.padding);
4019 return contentSizeWithPadding.expandedTo(bgSize);
4020 }
4021 break;
4022#endif // QT_CONFIG(tabwidget)
4023#if QT_CONFIG(groupbox)
4024 case CT_GroupBox:
4025 if (const auto *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
4026 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::GroupBox,
4027 opt->state);
4028 if (!resolved.isValid())
4029 break;
4030 const auto &metrics = *resolved.metrics;
4031 const QSize bgSize = metrics.bgImplicitSize
4032 + QSize(metrics.margins.left() + metrics.margins.right(),
4033 metrics.margins.top() + metrics.margins.bottom());
4034 const QSize contentSize = contentsSize.grownBy(metrics.padding);
4035 const QSize textSize = opt->fontMetrics
4036 .size(Qt::TextShowMnemonic, groupBox->text)
4037 .grownBy(metrics.textPadding);
4038 return textSize.expandedTo(contentSize).expandedTo(bgSize);
4039 }
4040 break;
4041#endif // QT_CONFIG(groupbox)
4042#if QT_CONFIG(scrollbar)
4043 case CT_ScrollBar:
4044 if (qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
4045 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ScrollBar, opt->state);
4046 if (!resolved.isValid())
4047 break;
4048 const auto &metrics = *resolved.metrics;
4049
4050 const int iH = metrics.indicatorImplicitSize.height()
4051 + metrics.padding.top() + metrics.padding.bottom()
4052 + metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
4053 const int iW = metrics.indicatorImplicitSize.width()
4054 + metrics.padding.left() + metrics.padding.right()
4055 + metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
4056 const int bgH = metrics.bgImplicitSize.height()
4057 + metrics.margins.top() + metrics.margins.bottom();
4058 const int bgW = metrics.bgImplicitSize.width()
4059 + metrics.margins.left() + metrics.margins.right();
4060 return QSize(std::max(iW, bgW), std::max(iH, bgH));
4061 }
4062 break;
4063#endif // QT_CONFIG(scrollbar)
4064#if QT_CONFIG(menu)
4065 case CT_MenuItem:
4066 if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
4067 // The menu dictates the item width
4068 // QMenu uses for PM_MenuHMargin for both left/right paddings,
4069 // so the available width is bgImplicitWidth + margins - 2 * padding.left.
4070 const auto menuType = widget && widget->inherits("QComboBoxPrivateContainer")
4071 ? QQStyleKitReader::ControlType::Popup
4072 : QQStyleKitReader::ControlType::Menu;
4073 const auto menuResolved = d->resolveLayout(menuType, opt->state);
4074 int minMenuW = 0;
4075 if (menuResolved.isValid()) {
4076 const auto &mm = *menuResolved.metrics;
4077 minMenuW = mm.bgImplicitSize.width()
4078 + mm.margins.left() + mm.margins.right() - 2 * mm.padding.left();
4079 }
4080
4081 if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
4082 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuSeparator,
4083 opt->state);
4084 if (!resolved.isValid())
4085 break;
4086 const auto &m = *resolved.metrics;
4087 const int bgWidth = m.bgImplicitSize.width()
4088 + m.margins.left() + m.margins.right();
4089 const int bgHeight = m.bgImplicitSize.height()
4090 + m.margins.top() + m.margins.bottom();
4091 return QSize(std::max(bgWidth, minMenuW), bgHeight);
4092 }
4093
4094 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuItem, opt->state);
4095 if (!resolved.isValid())
4096 break;
4097 const auto &m = *resolved.metrics;
4098 const auto *indicator = resolved.indicator();
4099 const auto *first = indicator ? indicator->first() : nullptr;
4100 const auto *second = indicator ? indicator->second() : nullptr;
4101 int indicatorW = 0;
4102 int indicatorH = 0;
4103 using QtPrivate::qSaturateRound;
4104 if (first && menuItem->checkType != QStyleOptionMenuItem::NotCheckable) {
4105 indicatorW += qSaturateRound(
4106 first->leftMargin() + first->width() + first->rightMargin()) + m.spacing;
4107 indicatorH = std::max(
4108 qSaturateRound(first->topMargin() + first->bottomMargin() + first->height()),
4109 indicatorH);
4110 }
4111 if (second && menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {
4112 indicatorW += qSaturateRound(
4113 second->leftMargin() + second->width() + second->rightMargin()) + m.spacing;
4114 indicatorH = std::max(
4115 qSaturateRound(second->topMargin() + second->bottomMargin() + second->height()),
4116 indicatorH);
4117 }
4118 const int bgH = m.bgImplicitSize.height() + m.margins.top() + m.margins.bottom();
4119 const int iconReserved = menuItem->maxIconWidth > 0
4120 ? menuItem->maxIconWidth + m.spacing
4121 : 0;
4122 const int contentW = contentsSize.width()
4123 + m.padding.left() + m.padding.right()
4124 + m.textPadding.left() + m.textPadding.right()
4125 + indicatorW + iconReserved;
4126 const int contentH = contentsSize.height()
4127 + m.padding.top() + m.padding.bottom()
4128 + m.textPadding.top() + m.textPadding.bottom();
4129 return QSize(std::max(contentW, minMenuW), std::max({ contentH, indicatorH, bgH }));
4130 }
4131 break;
4132 case CT_Menu: {
4133 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Menu, opt->state);
4134 if (!resolved.isValid())
4135 break;
4136 const auto &m = *resolved.metrics;
4137 const int height = std::max(contentsSize.height(), m.bgImplicitSize.height())
4138 + m.margins.top() + m.margins.bottom();
4139 return QSize(contentsSize.width(), height);
4140 }
4141#endif // QT_CONFIG(menu)
4142#if QT_CONFIG(menubar)
4143 case CT_MenuBar: {
4144 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuBar, opt->state);
4145 if (!resolved.isValid())
4146 break;
4147 const auto &m = *resolved.metrics;
4148 const int bgWidth = m.bgImplicitSize.width() + m.margins.left() + m.margins.right();
4149 const int bgHeight = m.bgImplicitSize.height() + m.margins.top() + m.margins.bottom();
4150 const int contentW = contentsSize.width() + m.padding.left() + m.padding.right();
4151 const int contentH = contentsSize.height() + m.padding.top() + m.padding.bottom();
4152 return QSize(std::max(contentW, bgWidth), std::max(contentH, bgHeight));
4153 }
4154 case CT_MenuBarItem: {
4155 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuBarItem,
4156 opt->state);
4157 if (!resolved.isValid())
4158 break;
4159 const auto &m = *resolved.metrics;
4160 const int bgWidth = m.bgImplicitSize.width() + m.margins.left() + m.margins.right();
4161 const int bgHeight = m.bgImplicitSize.height() + m.margins.top() + m.margins.bottom();
4162 const int contentW = contentsSize.width()
4163 + m.padding.left() + m.padding.right()
4164 + m.textPadding.left() + m.textPadding.right();
4165 const int contentH = contentsSize.height()
4166 + m.padding.top() + m.padding.bottom()
4167 + m.textPadding.top() + m.textPadding.bottom();
4168 return QSize(std::max(contentW, bgWidth), std::max(contentH, bgHeight));
4169 }
4170#endif // QT_CONFIG(menubar)
4171 default:
4172 break;
4173 }
4174 return QCommonStyle::sizeFromContents(ct, opt, contentsSize, widget);
4175}
4176
4177/*! \reimp */
4178int QStyleKitStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *widget) const
4179{
4180 Q_D(const QStyleKitStyle);
4181 switch (m) {
4182#if QT_CONFIG(menu)
4183 case PM_MenuPanelWidth:
4184 // The panel border is drawn as part of the resolved background (see PE_PanelMenu);
4185 return 0;
4186 case PM_MenuTearoffHeight:
4187 case PM_MenuScrollerHeight:
4188 return 0;
4189 case PM_MenuHMargin:
4190 case PM_MenuVMargin: {
4191 const auto controlType = widget && widget->inherits("QComboBoxPrivateContainer")
4192 ? QQStyleKitReader::ControlType::Popup
4193 : QQStyleKitReader::ControlType::Menu;
4194 const auto resolved = d->resolveLayout(controlType, opt ? opt->state : QStyle::State_None);
4195 if (!resolved.isValid())
4196 break;
4197 return m == PM_MenuHMargin ? resolved.metrics->padding.left()
4198 : resolved.metrics->padding.top();
4199 }
4200#endif
4201#if QT_CONFIG(menubar)
4202 case PM_MenuBarItemSpacing: {
4203 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuBarItem,
4204 opt ? opt->state : QStyle::State_None);
4205 if (resolved.isValid())
4206 return resolved.metrics->spacing;
4207 break;
4208 }
4209#endif
4210#if QT_CONFIG(toolbar)
4211 case PM_ToolBarFrameWidth:
4212 // The frame is folded into the resolved background/margins (see CE_ToolBar);
4213 return 0;
4214 case PM_ToolBarItemMargin: {
4215 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolBar,
4216 opt ? opt->state : QStyle::State_None);
4217 if (!resolved.isValid())
4218 break;
4219 return resolved.metrics->padding.left();
4220 }
4221 case PM_ToolBarItemSpacing: {
4222 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolBar,
4223 opt ? opt->state : QStyle::State_None);
4224 if (resolved.isValid())
4225 return resolved.metrics->spacing;
4226 break;
4227 }
4228 case PM_ToolBarHandleExtent: {
4229 // The only lever available to honor the style's background width/height
4230 // QToolBarLayout uses this to calculate the minimum size of the toolbar
4231 const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolBar,
4232 opt ? opt->state : QStyle::State_None);
4233 if (!resolved.isValid())
4234 break;
4235 const auto &metrics = *resolved.metrics;
4236 const bool horizontal = !opt || (opt->state & State_Horizontal);
4237 const int crossImplicit = horizontal
4238 ? metrics.bgImplicitSize.height()
4239 : metrics.bgImplicitSize.width();
4240 const int crossMargins = horizontal
4241 ? metrics.margins.top() + metrics.margins.bottom()
4242 : metrics.margins.left() + metrics.margins.right();
4243 const int crossPadding = horizontal
4244 ? metrics.padding.top() + metrics.padding.bottom()
4245 : metrics.padding.left() + metrics.padding.right();
4246 return qMax(0, crossImplicit + crossMargins - crossPadding);
4247 }
4248#endif // QT_CONFIG(toolbar)
4249#if QT_CONFIG(tabbar)
4250 case PM_TabBarTabHSpace:
4251 case PM_TabBarTabVSpace:
4252 case PM_TabBarTabOverlap:
4253 // Not supported in StyleKit
4254 case PM_TabCloseIndicatorWidth:
4255 case PM_TabCloseIndicatorHeight:
4256 return 0;
4257#endif // QT_CONFIG(tabbar)
4258 default:
4259 break;
4260 }
4261 return QCommonStyle::pixelMetric(m, opt, widget);
4262}
4263
4264/*! \reimp */
4265int QStyleKitStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
4266 QStyleHintReturn *shret) const
4267{
4268 switch (sh) {
4269 // keep consistent with the Controls style behavior
4270 case SH_SpinBox_SelectOnStep:
4271 case SH_ToolBar_Movable:
4272 case SH_TabBar_PreferNoArrows:
4273 return 0;
4274 case SH_Menu_MouseTracking:
4275 case SH_MenuBar_MouseTracking:
4276 case SH_Menu_Scrollable:
4277 return 1;
4278 default:
4279 break;
4280 }
4281 return QCommonStyle::styleHint(sh, opt, w, shret);
4282}
4283
4284/*! \reimp */
4285QPalette QStyleKitStyle::standardPalette() const
4286{
4287 return QCommonStyle::standardPalette();
4288}
4289
4290/*! \reimp */
4291void QStyleKitStyle::polish(QWidget *widget)
4292{
4293 if (!widget)
4294 return;
4295
4296 Q_D(QStyleKitStyle);
4297
4298 // When no user style is loaded, create the empty fallback style now,
4299 // before any reader is created, so that all widgets are styled consistently
4300 if (!d->style)
4301 d->ensureDefaultStyle();
4302
4303 widget->setAttribute(Qt::WA_Hover);
4304
4305#if QT_CONFIG(scrollbar)
4306 // QScrollBar sets WA_OpaquePaintEvent in its constructor, which skips
4307 // background erase before paint. This can leave artifacts when the
4308 // style's background is invisible, so disable it
4309 if (qobject_cast<QScrollBar *>(widget))
4310 widget->setAttribute(Qt::WA_OpaquePaintEvent, false);
4311#endif
4312
4313 // Create per-widget reader for interactive controls (transitions)
4314 const bool isInteractiveControl = false
4315#if QT_CONFIG(pushbutton)
4316 || qobject_cast<const QPushButton *>(widget)
4317#endif
4318#if QT_CONFIG(checkbox)
4319 || qobject_cast<const QCheckBox *>(widget)
4320#endif
4321#if QT_CONFIG(radiobutton)
4322 || qobject_cast<const QRadioButton *>(widget)
4323#endif
4324#if QT_CONFIG(combobox)
4325 || qobject_cast<const QComboBox *>(widget)
4326 || (widget && widget->inherits("QComboBoxPrivateContainer"))
4327#endif
4328#if QT_CONFIG(lineedit)
4329 || qobject_cast<const QLineEdit *>(widget)
4330#endif
4331#if QT_CONFIG(textedit)
4332 || qobject_cast<const QTextEdit *>(widget)
4333 || qobject_cast<const QPlainTextEdit *>(widget)
4334#endif
4335#if QT_CONFIG(label)
4336 || qobject_cast<const QLabel *>(widget)
4337#endif
4338#if QT_CONFIG(progressbar)
4339 || qobject_cast<const QProgressBar *>(widget)
4340#endif
4341#if QT_CONFIG(slider)
4342 || qobject_cast<const QSlider *>(widget)
4343#endif
4344#if QT_CONFIG(scrollbar)
4345 || qobject_cast<const QScrollBar *>(widget)
4346#endif
4347#if QT_CONFIG(spinbox)
4348 || qobject_cast<const QSpinBox *>(widget)
4349#endif
4350#if QT_CONFIG(tabbar)
4351 || qobject_cast<const QTabBar *>(widget)
4352#endif
4353#if QT_CONFIG(tabwidget)
4354 || qobject_cast<const QTabWidget *>(widget)
4355#endif
4356#if QT_CONFIG(menu)
4357 || qobject_cast<const QMenu *>(widget)
4358 || qobject_cast<const QMenuBar *>(widget)
4359#endif
4360 ;
4361 if (isInteractiveControl)
4362 d->readerForWidget(widget);
4363
4364 // Disable the viewport's autoFillBackground so the styled background shows through.
4365 // Only flip it when it was enabled, and store the widget so unpolish() can restore it
4366 // (e.g. when the application switches to a non-StyleKit style).
4367 if (QWidget *vp = managedViewport(widget); vp && vp->autoFillBackground()) {
4368 vp->setAutoFillBackground(false);
4369 d->bgFillDisabledWidgets.insert(widget);
4370 }
4371#if QT_CONFIG(menu)
4372 if (qobject_cast<QMenu *>(widget)
4373# if QT_CONFIG(combobox)
4374 || widget->inherits("QComboBoxPrivateContainer")
4375# endif
4376 ) {
4377 // Enable translucent backgrounds so that we can draw rounded corners on popup menus.
4378 // Store it in bgFillDisabledWidgets so that we can restore it in unpolish()
4379 if (!widget->testAttribute(Qt::WA_TranslucentBackground)) {
4380 widget->setAttribute(Qt::WA_TranslucentBackground, true);
4381 d->bgFillDisabledWidgets.insert(widget);
4382 }
4383 }
4384#endif
4385
4386#if QT_CONFIG(lineedit)
4387 if (auto *lineEdit = qobject_cast<QLineEdit *>(widget)) {
4388 if (!lineEdit->property("_q_stylekit_alignment_set").toBool()) {
4389 QQStyleKitReader *r = d->readerForWidget(widget);
4390 if (r) {
4391 const QWidget *target = containerWidget(widget);
4392 QQStyleKitReader::ControlType ct = controlTypeForWidget(target);
4393 r->setControlType(ct);
4394 const auto *textProps = r->global()->text();
4395 if (textProps) {
4396 const uint align = resolvedAlignment(
4397 textProps->alignment(), Qt::AlignLeft, Qt::AlignVCenter);
4398 lineEdit->setAlignment(Qt::Alignment(align));
4399 }
4400 }
4401 }
4402 }
4403#endif
4404
4405 d->refreshStyleFont(widget);
4406 d->refreshStylePalette(widget);
4407
4408 bool needsEventFilter = isSelfPaintingWidget(widget);
4409#if QT_CONFIG(menu)
4410 // Menus are transient: drop their sub-element tracking when they hide
4411 needsEventFilter = needsEventFilter || qobject_cast<QMenu *>(widget);
4412#endif
4413 if (needsEventFilter)
4414 widget->installEventFilter(this);
4415
4416 QCommonStyle::polish(widget);
4417}
4418
4419/*! \reimp */
4420void QStyleKitStyle::polish(QApplication *app)
4421{
4422 QCommonStyle::polish(app);
4423}
4424
4425/*! \reimp */
4426void QStyleKitStyle::polish(QPalette &palette)
4427{
4428 QCommonStyle::polish(palette);
4429}
4430
4431/*! \reimp */
4432void QStyleKitStyle::unpolish(QWidget *widget)
4433{
4434 Q_D(QStyleKitStyle);
4435 d->unsetStylePalette(widget);
4436 d->unsetStyleFont(widget);
4437 bool hasEventFilter = isSelfPaintingWidget(widget);
4438#if QT_CONFIG(menu)
4439 hasEventFilter = hasEventFilter || qobject_cast<QMenu *>(widget);
4440#endif
4441 if (hasEventFilter)
4442 widget->removeEventFilter(this);
4443 if (d->bgFillDisabledWidgets.remove(widget)) {
4444 if (QWidget *vp = managedViewport(widget))
4445 vp->setAutoFillBackground(true);
4446#if QT_CONFIG(menu)
4447 else if (qobject_cast<QMenu *>(widget)
4448# if QT_CONFIG(combobox)
4449 || widget->inherits("QComboBoxPrivateContainer")
4450# endif
4451 ) {
4452 widget->setAttribute(Qt::WA_TranslucentBackground, false);
4453 }
4454#endif
4455 }
4456 d->cleanupWidgetReader(widget);
4457#if QT_CONFIG(scrollbar)
4458 if (qobject_cast<QScrollBar *>(widget))
4459 widget->setAttribute(Qt::WA_OpaquePaintEvent);
4460#endif
4461 QCommonStyle::unpolish(widget);
4462}
4463
4464/*! \reimp */
4465void QStyleKitStyle::unpolish(QApplication *app)
4466{
4467 Q_D(QStyleKitStyle);
4468 d->clearMetricsCache();
4469 QCommonStyle::unpolish(app);
4470}
4471
4472/*! \reimp */
4473bool QStyleKitStyle::eventFilter(QObject *obj, QEvent *event)
4474{
4475 Q_D(QStyleKitStyle);
4476 switch (event->type()) {
4477 case QEvent::EnabledChange:
4478 case QEvent::HoverEnter:
4479 case QEvent::HoverLeave:
4480 case QEvent::FocusIn:
4481 case QEvent::FocusOut:
4482 if (auto *w = qobject_cast<QWidget *>(obj)) {
4483 if (d->customPaletteWidgets.contains(w))
4484 d->refreshStylePalette(w);
4485 if (d->customFontWidgets.contains(w))
4486 d->refreshStyleFont(w);
4487 }
4488 break;
4489#if QT_CONFIG(menu)
4490 case QEvent::Hide:
4491 if (auto *menu = qobject_cast<QMenu *>(obj))
4492 d->cleanupSubElements(menu);
4493 break;
4494#endif
4495 default:
4496 break;
4497 }
4498 return QCommonStyle::eventFilter(obj, event);
4499}
4500
4501/*! \reimp */
4502bool QStyleKitStyle::event(QEvent *event)
4503{
4504 return QCommonStyle::event(event);
4505}
4506
4507QT_END_NAMESPACE
4508
4509#include "moc_qstylekitstyle.cpp"
Combined button and popup list for selecting options.
static QWidget * paintTarget(const QWidget *widget)
static bool applyDelegateTransform(QPainter *painter, const QQStyleKitDelegateProperties *element, const QRectF &box)
static QWidget * managedViewport(QWidget *widget)
static QMargins elementMargins(const QQStyleKitDelegateProperties *element)
static uint resolvedAlignment(uint raw, Qt::Alignment hDefault, Qt::Alignment vDefault)
static qreal resolvedWidth(const QQStyleKitDelegateProperties *element, qreal availableW)
static bool isSelfPaintingWidget(const QWidget *widget)
static qreal resolvedHeight(const QQStyleKitDelegateProperties *element, qreal availableH)
static QUrl urlFromStylePath(const QString &filePath)
static const QWidget * containerWidget(const QWidget *w)