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