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