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