Source code

Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qstylekitstyle.h"
#include "qstylekitstyle_p.h"
#include <QtWidgets/qstyleoption.h>
#include <QtWidgets/qstyle.h>
#include <QtWidgets/qapplication.h>
#if QT_CONFIG(scrollarea)
# include <QtWidgets/qabstractscrollarea.h>
#endif
#if QT_CONFIG(itemviews)
# include <QtWidgets/qabstractitemview.h>
#endif
#if QT_CONFIG(lineedit)
# include <QtWidgets/qlineedit.h>
#endif
#if QT_CONFIG(pushbutton)
# include <QtWidgets/qpushbutton.h>
#endif
#if QT_CONFIG(checkbox)
# include <QtWidgets/qcheckbox.h>
#endif
#if QT_CONFIG(radiobutton)
# include <QtWidgets/qradiobutton.h>
#endif
#if QT_CONFIG(combobox)
# include <QtWidgets/qcombobox.h>
#endif
#if QT_CONFIG(slider)
# include <QtWidgets/qslider.h>
#endif
#if QT_CONFIG(dial)
# include <QtWidgets/qdial.h>
#endif
#if QT_CONFIG(scrollbar)
# include <QtWidgets/qscrollbar.h>
#endif
#if QT_CONFIG(spinbox)
# include <QtWidgets/qspinbox.h>
#endif
#if QT_CONFIG(progressbar)
# include <QtWidgets/qprogressbar.h>
#endif
#if QT_CONFIG(textedit)
# include <QtWidgets/qtextedit.h>
# include <QtWidgets/qplaintextedit.h>
#endif
#if QT_CONFIG(tabbar)
# include <QtWidgets/qtabbar.h>
#endif
#if QT_CONFIG(tabwidget)
# include <QtWidgets/qtabwidget.h>
#endif
#if QT_CONFIG(toolbar)
# include <QtWidgets/qtoolbar.h>
#endif
#if QT_CONFIG(toolbutton)
# include <QtWidgets/qtoolbutton.h>
#endif
#if QT_CONFIG(groupbox)
# include <QtWidgets/qgroupbox.h>
#endif
#if QT_CONFIG(menu)
# include <QtWidgets/qmenu.h>
# include <QtWidgets/qmenubar.h>
#endif
#if QT_CONFIG(label)
# include <QtWidgets/qlabel.h>
#endif
#include <QtWidgets/private/qwidget_p.h>
#include <QtCore/private/qobject_p.h>
#include <QtGui/qpainter.h>
#include <QtGui/qpainterpath.h>
#include <QtGui/qpainterstateguard.h>
#include <QtGui/qstylehints.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qnumeric.h>
#include <QtQml/private/qqmlcomponent_p.h>
#include <QtQml/qqmlengine.h>
#include <QtQuick/private/qquicktransition_p.h>
#include <QtLabsStyleKit/private/qqstylekit_p.h>
#include <QtLabsStyleKit/private/qqstylekitcontrolproperties_p.h>
#include <QtLabsStyleKit/private/qqstylekitstyle_p.h>
QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(lcStyleKit, "qt.labs.stylekit")
/*!
\class QStyleKitStyle
\inmodule QtLabsStyleKit
\ingroup appearance
\since 6.12
\preliminary
\brief The QStyleKitStyle class applies a \l {Qt Labs StyleKit} style
to Qt Widgets.
QStyleKitStyle is a QStyle implementation that uses a \l StyleKit \l Style
to style Qt Widgets. The \l Style is a QML file that declaratively describes
the visual design (colors, sizes, radii, borders, and other properties)
for each widget type and state. QStyleKitStyle uses those property values
to paint each widget entirely with QPainter. Qt Quick and the scene graph
play no part in the rendering.
This separation means the same \l Style QML file can drive both
Qt Quick Controls and Qt Widgets, sharing one design definition across
both systems.
\note StyleKit is a Qt Labs module, and its API may change between
Qt releases.
\section1 Loading a Style
A style is a QML file whose root object is a \l Style. To load it,
pass the file path to the constructor or to \l setStylePath():
\if !defined(qtforpython)
\code
auto *style = new QStyleKitStyle(QStringLiteral(":/styles/MyStyle.qml"));
QApplication::setStyle(style);
\endcode
\else
\code
style = QStyleKitStyle(":/styles/MyStyle.qml")
QApplication.setStyle(style)
\endcode
\endif
QStyleKitStyle loads the Style with an internal QQmlEngine that the
QStyleKitStyle instance owns. If the path is invalid or the root object
is not a \l Style, QStyleKitStyle emits a warning and keeps the
previously loaded style. Until a style loads successfully, the widgets
use an empty fallback style.
\section1 Themes
A Style can define one or more named \l {Theme}{themes}. Call
\l setThemeName() to select the active theme, and read
\l availableThemeNames to list the themes a Style offers. The special
theme name \c System makes the style follow the platform color scheme:
when the OS color scheme changes, QStyleKitStyle recreates the active
theme and repolishes all widgets.
\section1 Widget to StyleKit control mapping
Each Qt Widgets class is mapped to a StyleKit control type, which
determines which control entry in the \l Style applies to it. Use the
corresponding control entry to configure visual properties for that
widget type, including individual parts of the widget such as its
background, indicator, and handle. See \l ControlStyleProperties for the
full list of stylable properties.
Properties not set in a specific control entry fall back through the
control type hierarchy: for example, \c button falls back to
\c abstractButton, which falls back to \c control.
\table
\header
\li Qt Widgets class
\li StyleKit control
\row
\li QPushButton (flat)
\li \l {StylableControls::flatButton}{flatButton}
\row
\li QPushButton
\li \l {StylableControls::button}{button}
\row
\li QCheckBox
\li \l {StylableControls::checkBox}{checkBox}
\row
\li QRadioButton
\li \l {StylableControls::radioButton}{radioButton}
\row
\li QComboBox
\li \l {StylableControls::comboBox}{comboBox}
\row
\li QSlider
\li \l {StylableControls::slider}{slider}
\row
\li QDial
\li \l {StylableControls::dial}{dial}
\row
\li QScrollBar
\li \l {StylableControls::scrollBar}{scrollBar}
\row
\li QSpinBox, QDoubleSpinBox
\li \l {StylableControls::spinBox}{spinBox}
\row
\li QProgressBar
\li \l {StylableControls::progressBar}{progressBar}
\row
\li QLineEdit
\li \l {StylableControls::textField}{textField}
\row
\li QTextEdit, QPlainTextEdit
\li \l {StylableControls::textArea}{textArea}
\row
\li QTabBar
\li \l {StylableControls::tabBar}{tabBar}
\row
\li QTabWidget
\li \l {StylableControls::page}{page}
\row
\li QToolBar
\li \l {StylableControls::toolBar}{toolBar}
\row
\li QToolButton
\li \l {StylableControls::toolButton}{toolButton}
\row
\li QGroupBox
\li \l {StylableControls::groupBox}{groupBox}
\row
\li QFrame
\li \l {StylableControls::frame}{frame}
\row
\li QLabel
\li \l {StylableControls::label}{label}
\row
\li QMenu
\li \l {StylableControls::menu}{menu}
\row
\li QMenuBar
\li \l {StylableControls::menuBar}{menuBar}
\row
\li Everything else
\li \l {StylableControls::control}{control}
\endtable
QStyleKitStyle does not yet support widgets outside this table;
\l QCommonStyle paints them instead. Conversely, some control entries in
\l StylableControls have no Qt Widgets equivalent, and
QStyleKitStyle ignores them when styling widgets.
\section2 Sub-controls within a widget
Each sub-control of a widget maps to its own control entry in the
\l Style, which lets you style the sub-controls individually.
\table
\header
\li Sub-control
\li StyleKit control
\row
\li \l QStyledItemDelegate items -- the default delegate for all Qt item views,
including the \l QComboBox popup list
\li \l {StylableControls::itemDelegate}{itemDelegate}
\row
\li The same items, when user-checkable (that is, when they show
a check indicator)
\li \l {StylableControls::checkDelegate}{checkDelegate};
falls back to \c itemDelegate for anything not set explicitly
\row
\li Individual tabs in a \l QTabBar
\li \l {StylableControls::tabButton}{tabButton}
\row
\li \l QMenu items
\li \l {StylableControls::menuItem}{menuItem}
\row
\li Separators in a \l QMenu
\li \l {StylableControls::menuSeparator}{menuSeparator}
\row
\li \l QMenuBar items
\li \l {StylableControls::menuBarItem}{menuBarItem}
\row
\li Separators in a \l QToolBar
\li \l {StylableControls::toolSeparator}{toolSeparator}
\row
\li The \l QComboBox popup list container
\li \l {StylableControls::popup}{popup}
\endtable
\section1 Known limitations
QStyleKitStyle does not support the following StyleKit features:
\list
\li \b{Shadows} -- QStyleKitStyle ignores shadow definitions.
\li \b{Delegate scale above 1.0 on a control's background} -- a
widget cannot paint outside its own rectangle, so QStyleKitStyle
clips the scaled background at the widget edge. Use
\l {DelegateStyle::}{margins} to inset the background and reserve
room for it to grow. Scaling indicators, handles, and foregrounds
still works.
\li \b{Variations} -- QStyleKitStyle does not yet apply a
\l StyleVariation set on a widget instance.
\li \b{Custom controls} -- QStyleKitStyle does not yet style custom
widgets declared with \l CustomControl.
\li \b{Custom delegates} -- QStyleKitStyle ignores the
\l {DelegateStyle::}{delegate} property and always uses its
built-in rendering.
\endlist
\sa QStyle, QCommonStyle, {Qt Labs StyleKit}, Style, Theme
*/
/*!
\property QStyleKitStyle::stylePath
\brief the path to the QML \l Style file driving this style.
The value is a path to a local file or to a file in the resource file
system, (for example, \c{:/styles/MyStyle.qml} or \c{qrc:/qt/qml/MyQmlModule/Style.qml}).
QStyleKitStyle resolves a relative path against the application's working directory.
The file must contain a QML component whose root object is a \l Style.
Setting this property reloads the style. If the new file does not load,
QStyleKitStyle emits a warning and keeps the previously loaded style
and style path.
*/
/*!
\property QStyleKitStyle::themeName
\brief the name of the active theme.
The value must be one of the entries in \l availableThemeNames. That
list includes the special name \c System, which makes the style follow
the platform color scheme. The comparison ignores case.
Setting this property repolishes all widgets so that they repaint with
the new theme. If the name matches no theme, the \l Style emits a
warning and applies no theme.
*/
/*!
\property QStyleKitStyle::availableThemeNames
\brief the list of theme names exposed by the loaded \l Style.
This list always includes \c System, which follows the platform color
scheme. It also includes \c Light and \c Dark when the \l Style defines
those themes, together with any custom themes that the style author
defines. The list is empty when no style is loaded.
*/
/*!
\property QStyleKitStyle::customThemeNames
\brief the list of custom theme names defined by the loaded \l Style.
Unlike \l availableThemeNames, this list excludes \c System, \c Light,
and \c Dark, and contains only the themes that the style author
defines explicitly. The list is empty when no style is loaded.
\sa availableThemeNames, themeName
*/
static QQStyleKitReader::ControlType controlTypeForWidget(const QWidget *widget)
{
if (!widget)
return QQStyleKitReader::Control;
#if QT_CONFIG(pushbutton)
if (qobject_cast<const QPushButton *>(widget))
return QQStyleKitReader::Button;
#endif
#if QT_CONFIG(checkbox)
if (qobject_cast<const QCheckBox *>(widget))
return QQStyleKitReader::CheckBox;
#endif
#if QT_CONFIG(radiobutton)
if (qobject_cast<const QRadioButton *>(widget))
return QQStyleKitReader::RadioButton;
#endif
#if QT_CONFIG(combobox)
if (qobject_cast<const QComboBox *>(widget))
return QQStyleKitReader::ComboBox;
#endif
#if QT_CONFIG(slider)
if (qobject_cast<const QSlider *>(widget))
return QQStyleKitReader::Slider;
#endif
#if QT_CONFIG(scrollbar)
if (qobject_cast<const QScrollBar *>(widget))
return QQStyleKitReader::ScrollBar;
#endif
#if QT_CONFIG(spinbox)
if (qobject_cast<const QSpinBox *>(widget) || qobject_cast<const QDoubleSpinBox *>(widget))
return QQStyleKitReader::SpinBox;
#endif
#if QT_CONFIG(itemviews)
if (qobject_cast<const QAbstractItemView *>(widget))
return QQStyleKitReader::ItemDelegate;
#endif
#if QT_CONFIG(progressbar)
if (qobject_cast<const QProgressBar *>(widget))
return QQStyleKitReader::ProgressBar;
#endif
#if QT_CONFIG(lineedit)
if (qobject_cast<const QLineEdit *>(widget))
return QQStyleKitReader::TextField;
#endif
#if QT_CONFIG(textedit)
if (qobject_cast<const QTextEdit *>(widget) || qobject_cast<const QPlainTextEdit *>(widget))
return QQStyleKitReader::TextArea;
#endif
#if QT_CONFIG(tabbar)
if (qobject_cast<const QTabBar *>(widget))
return QQStyleKitReader::TabBar;
#endif
#if QT_CONFIG(tabwidget)
if (qobject_cast<const QTabWidget *>(widget))
return QQStyleKitReader::Page;
#endif
#if QT_CONFIG(toolbar)
if (qobject_cast<const QToolBar *>(widget))
return QQStyleKitReader::ToolBar;
#endif
#if QT_CONFIG(toolbutton)
if (qobject_cast<const QToolButton *>(widget))
return QQStyleKitReader::ToolButton;
#endif
#if QT_CONFIG(groupbox)
if (qobject_cast<const QGroupBox *>(widget))
return QQStyleKitReader::GroupBox;
#endif
#if QT_CONFIG(menu)
if (qobject_cast<const QMenuBar *>(widget))
return QQStyleKitReader::MenuBar;
if (qobject_cast<const QMenu *>(widget))
return QQStyleKitReader::Menu;
#endif
#if QT_CONFIG(tooltip)
if (widget->inherits("QTipLabel"))
return QQStyleKitReader::ToolTip;
#endif
if (widget->windowType() == Qt::Popup)
return QQStyleKitReader::Popup;
if (widget->windowType() == Qt::Dialog)
return QQStyleKitReader::Dialog;
if (widget->windowType() == Qt::Window)
return QQStyleKitReader::ApplicationWindow;
#ifndef QT_NO_FRAME
if (qobject_cast<const QFrame *>(widget))
return QQStyleKitReader::Frame;
#endif
#if QT_CONFIG(dial)
if (qobject_cast<const QDial *>(widget))
return QQStyleKitReader::Dial;
#endif
return QQStyleKitReader::Control;
}
#if QT_CONFIG(itemviews)
static QQStyleKitReader::ControlType itemViewControlType(const QStyleOption *opt)
{
const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt);
return itemViewOption && (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator)
? QQStyleKitReader::ControlType::CheckDelegate
: QQStyleKitReader::ControlType::ItemDelegate;
}
#endif
// Some widgets (like QAbstractScrollArea) paint on a child widget (the viewport)
// instead of on themselves. This function returns the widget that is actually painted on,
// which is the one that should be updated when the style values change.
static QWidget *paintTarget(const QWidget *widget)
{
#if QT_CONFIG(scrollarea)
if (const auto *area = qobject_cast<const QAbstractScrollArea *>(widget))
return area->viewport();
#endif
return const_cast<QWidget *>(widget);
}
// Returns true for widgets that draw themselves using their widget font and
// palette directly, bypassing the style's drawControl path.
static bool isSelfPaintingWidget(const QWidget *widget)
{
return false
#if QT_CONFIG(label)
|| qobject_cast<const QLabel *>(widget)
#endif
#if QT_CONFIG(textedit)
|| qobject_cast<const QTextEdit *>(widget)
|| qobject_cast<const QPlainTextEdit *>(widget)
#endif
#if QT_CONFIG(lineedit)
|| qobject_cast<const QLineEdit *>(widget)
#endif
;
}
static QWidget *managedViewport(QWidget *widget)
{
#if QT_CONFIG(textedit)
if (auto *textEdit = qobject_cast<QTextEdit *>(widget))
return textEdit->viewport();
if (auto *plainTextEdit = qobject_cast<QPlainTextEdit *>(widget))
return plainTextEdit->viewport();
#endif
#if QT_CONFIG(itemviews) && QT_CONFIG(combobox)
if (auto *view = qobject_cast<QAbstractItemView *>(widget)) {
if (auto *p = view->parentWidget(); p && p->inherits("QComboBoxPrivateContainer"))
return view->viewport();
}
#endif
return nullptr;
}
static uint resolvedAlignment(uint raw, Qt::Alignment hDefault, Qt::Alignment vDefault)
{
uint result = 0;
const uint h = raw & Qt::AlignHorizontal_Mask;
const uint v = raw & Qt::AlignVertical_Mask;
result |= h ? h : uint(hDefault);
result |= v ? v : uint(vDefault);
return result;
}
static qreal resolvedWidth(const QQStyleKitDelegateProperties *element, qreal availableW)
{
return qMax(0.0, element->fillWidth() ? availableW : element->width());
}
static qreal resolvedHeight(const QQStyleKitDelegateProperties *element, qreal availableH)
{
return qMax(0.0, element->fillHeight() ? availableH : element->height());
}
static QMargins elementMargins(const QQStyleKitDelegateProperties *element)
{
using QtPrivate::qSaturateRound;
return QMargins(qSaturateRound(element->leftMargin()),
qSaturateRound(element->topMargin()),
qSaturateRound(element->rightMargin()),
qSaturateRound(element->bottomMargin()));
}
/*! \internal
Applies \a element's rotation and scale to \a painter about the centre of \a box.
Returns \c false if the scale is 0 or if \a element is null, otherwise returns \c true.
*/
static bool applyDelegateTransform(QPainter *painter,
const QQStyleKitDelegateProperties *element,
const QRectF &box)
{
if (!element)
return false;
const qreal scale = element->scale();
if (qFuzzyIsNull(scale))
return false;
const qreal rotation = element->rotation();
if (qFuzzyIsNull(rotation) && qFuzzyCompare(scale, 1.0))
return true;
const QPointF center = box.center();
painter->translate(center);
if (!qFuzzyIsNull(rotation))
painter->rotate(rotation);
if (!qFuzzyCompare(scale, 1.0))
painter->scale(scale, scale);
painter->translate(-center);
return true;
}
#if QT_CONFIG(dial)
static qreal dialAngle(const QStyleOptionSlider *dial)
{
const qreal range = dial->maximum - dial->minimum;
qreal position = range > 0 ? (dial->sliderPosition - dial->minimum) / range : 0;
if (!dial->upsideDown)
position = 1 - position;
const qreal start = dial->dialWrapping ? -180 : -150;
return start + position * -2 * start;
}
static QPointF dialHandleOffset(const QRectF &backgroundRect, const QQStyleKitDelegateProperties *handle)
{
// mirrors Dial.qml
return QPointF(handle->leftMargin() - handle->rightMargin(),
-qMin(backgroundRect.width(), backgroundRect.height()) / 2 + handle->height() / 2
+ handle->topMargin() - handle->bottomMargin());
}
#endif
// Copied from qstylesheetstyle.cpp
static const QWidget *containerWidget(const QWidget *w)
{
#if QT_CONFIG(lineedit)
if (qobject_cast<const QLineEdit *>(w)) {
// if the QLineEdit is an embeddedWidget, we need the real widget
QWidget *parent = w->parentWidget();
if (false
# if QT_CONFIG(combobox)
|| qobject_cast<const QComboBox *>(parent)
# endif
# if QT_CONFIG(spinbox)
|| qobject_cast<const QAbstractSpinBox *>(parent)
# endif
) {
return parent;
}
}
#endif
#if QT_CONFIG(scrollarea)
if (const QAbstractScrollArea *sa =
qobject_cast<const QAbstractScrollArea *>(w->parentWidget())) {
if (sa->viewport() == w)
return w->parentWidget();
}
#endif
return w;
}
QStyleKitStylePrivate::QStyleKitStylePrivate()
: QCommonStylePrivate()
{
}
static QUrl urlFromStylePath(const QString &filePath)
{
if (filePath.startsWith(QLatin1Char(':')))
return QUrl(QLatin1String("qrc") + filePath);
if (filePath.startsWith(QLatin1String("qrc:")) || filePath.startsWith(QLatin1String("file:")))
return QUrl(filePath);
return QUrl::fromLocalFile(filePath);
}
bool QStyleKitStylePrivate::loadStyle()
{
Q_Q(QStyleKitStyle);
if (!qmlEngine)
qmlEngine = new QQmlEngine(q);
if (!qmlEngine) {
qWarning("QStyleKitStyle: No QML engine available to load style.");
return false;
}
const QUrl url = stylePath.isEmpty() ? QUrl() : urlFromStylePath(stylePath);
if (stylePath.isEmpty() || !url.isValid()) {
qWarning("QStyleKitStyle: No valid style path provided: %s", qPrintable(stylePath));
return false;
}
QQmlComponent component(qmlEngine, url);
if (component.isError()) {
qWarning("QStyleKitStyle: Failed to load style from %s: %s", qPrintable(stylePath),
qPrintable(component.errorString()));
return false;
}
// Avoid creating anything other than StyleKit Style objects
// by checking the metaobject of the root type before creating the object
QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(&component);
const auto compilationUnit = componentPrivate->compilationUnit();
const auto propertyCache = compilationUnit ? compilationUnit->rootPropertyCache() : nullptr;
const auto firstMetaObject = propertyCache ? propertyCache->firstCppMetaObject() : nullptr;
if (!firstMetaObject || !firstMetaObject->inherits(&QQStyleKitStyle::staticMetaObject)) {
qWarning("QStyleKitStyle: Failed to load style from %s: component is not a StyleKit Style.",
qPrintable(stylePath));
return false;
}
QQStyleKitStyle *styleObject = qobject_cast<QQStyleKitStyle *>(component.create());
if (!styleObject) {
qWarning("QStyleKitStyle: Failed to create style object from %s: "
"component is not a StyleKit style.",
qPrintable(stylePath));
return false;
}
const bool isReload = style != nullptr;
delete style;
style = styleObject;
style->setParent(q);
if (!isReload) {
QObject::connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, q,
[this]() {
if (style && style->themeName() == QLatin1String("System")) {
style->recreateTheme();
updateStyle();
}
});
}
if (style->loaded())
QQStyleKitReader::resetReadersForStyle(style);
return true;
}
/*! \internal
Creates and returns the empty fallback style used when no user
style is loaded.
*/
QQStyleKitStyle *QStyleKitStylePrivate::ensureDefaultStyle()
{
if (defaultStyle)
return defaultStyle;
Q_Q(QStyleKitStyle);
if (!qmlEngine)
qmlEngine = new QQmlEngine(q);
QQmlComponent component(qmlEngine);
component.loadFromModule("Qt.labs.StyleKit", "Style");
if (component.isError()) {
qWarning("QStyleKitStyle: Failed to create default fallback style: %s",
qPrintable(component.errorString()));
return nullptr;
}
defaultStyle = qobject_cast<QQStyleKitStyle *>(component.create());
if (defaultStyle) {
defaultStyle->setParent(q);
qCDebug(lcStyleKit,
"No style set; using a default fallback style. "
"Set stylePath to load a StyleKit Style.");
}
return defaultStyle;
}
/*! \internal
Returns the user style if one is loaded, otherwise the default fallback
style
*/
QQStyleKitStyle *QStyleKitStylePrivate::effectiveStyle() const
{
return style ? style : defaultStyle;
}
void QStyleKitStylePrivate::updateStyle()
{
clearMetricsCache();
QQStyleKitStyle *effective = effectiveStyle();
if (sharedReader && sharedReader->style() != effective)
sharedReader->setExplicitStyle(effective);
if (subElementReader && subElementReader->style() != effective)
subElementReader->setExplicitStyle(effective);
clearAllSubElements();
for (auto *wr : std::as_const(widgetReaders)) {
if (wr->style() != effective)
wr->setExplicitStyle(effective);
}
if (!QWidgetPrivate::allWidgets)
return;
for (auto *widget : std::as_const(*QWidgetPrivate::allWidgets)) {
if (customFontWidgets.contains(widget))
unsetStyleFont(widget);
refreshStyleFont(widget);
if (customPaletteWidgets.contains(widget))
unsetStylePalette(widget);
refreshStylePalette(widget);
refreshStyleMinimumSize(widget);
}
QEvent styleChange(QEvent::StyleChange);
for (auto *widget : std::as_const(*QWidgetPrivate::allWidgets)) {
QApplication::sendEvent(widget, &styleChange);
widget->update();
}
}
void QStyleKitStylePrivate::refreshStyleMinimumSize(QWidget *widget)
{
// QDial::sizeHint() is fixed and never asks the style, so enforce
// the minimum size from the StyleKit style.
#if QT_CONFIG(dial)
if (!qobject_cast<QDial *>(widget))
return;
const auto resolved = resolveLayout(QQStyleKitReader::ControlType::Dial, QStyle::State_Enabled);
if (!resolved.isValid())
return;
const auto &m = *resolved.metrics;
const QSize styleSize(m.bgImplicitSize.width() + m.margins.left() + m.margins.right(),
m.bgImplicitSize.height() + m.margins.top() + m.margins.bottom());
auto it = customMinimumSizeWidgets.find(widget);
if (it == customMinimumSizeWidgets.end()) {
it = customMinimumSizeWidgets.insert(widget, widget->minimumSize());
QObject::connect(widget, &QObject::destroyed, widget,
[this, widget]() { customMinimumSizeWidgets.remove(widget); });
}
widget->setMinimumSize(it.value().expandedTo(styleSize));
#else
Q_UNUSED(widget);
#endif
}
void QStyleKitStylePrivate::unsetStyleMinimumSize(QWidget *widget)
{
const auto it = customMinimumSizeWidgets.constFind(widget);
if (it == customMinimumSizeWidgets.cend())
return;
const QSize original = it.value();
customMinimumSizeWidgets.erase(it);
widget->setMinimumSize(original);
}
void QStyleKitStylePrivate::setStyleAutoFill(QWidget *widget)
{
// Disable the viewport's autoFillBackground
// so the styled background shows through
if (QWidget *vp = managedViewport(widget); vp && vp->autoFillBackground()) {
vp->setAutoFillBackground(false);
autoFillDisabledWidgets.insert(widget);
}
}
void QStyleKitStylePrivate::unsetStyleAutoFill(QWidget *widget)
{
if (!autoFillDisabledWidgets.remove(widget))
return;
if (QWidget *vp = managedViewport(widget))
vp->setAutoFillBackground(true);
}
void QStyleKitStylePrivate::setStyleTranslucency(QWidget *widget)
{
const bool isPopup = false
#if QT_CONFIG(menu)
|| qobject_cast<QMenu *>(widget)
#endif
#if QT_CONFIG(combobox)
|| widget->inherits("QComboBoxPrivateContainer")
#endif
#if QT_CONFIG(tooltip)
|| widget->inherits("QTipLabel")
#endif
;
if (!isPopup || widget->testAttribute(Qt::WA_TranslucentBackground))
return;
// Enable translucency for popups so that we can draw rounded corners
widget->setAttribute(Qt::WA_TranslucentBackground, true);
widget->setWindowFlag(Qt::FramelessWindowHint, true);
translucentWidgets.insert(widget);
}
void QStyleKitStylePrivate::unsetStyleTranslucency(QWidget *widget)
{
if (!translucentWidgets.remove(widget))
return;
widget->setAttribute(Qt::WA_TranslucentBackground, false);
widget->setWindowFlag(Qt::FramelessWindowHint, false);
}
// Follow the approach in qstylesheetstyle.cpp
void QStyleKitStylePrivate::unsetStyleFont(QWidget *widget)
{
auto it = customFontWidgets.find(widget);
if (it == customFontWidgets.end())
return;
auto customFont = std::move(*it);
customFontWidgets.erase(it);
widget->setFont(std::move(customFont).reverted(QWidgetPrivate::get(widget)->localFont()));
}
void QStyleKitStylePrivate::setStyleFont(QWidget *widget, const QFont &styleFont)
{
if (!effectiveStyle() || !widget)
return;
const auto styleMask = styleFont.resolveMask();
auto it = customFontWidgets.find(widget);
if (it == customFontWidgets.end()) {
if (styleMask == 0)
return;
it = customFontWidgets.insert(widget, { QWidgetPrivate::get(widget)->localFont(), 0 });
QObject::connect(widget, &QObject::destroyed, widget,
[this, widget]() { customFontWidgets.remove(widget); });
}
it->resolveMask = styleMask;
const QFont &baseline = it->oldWidgetValue;
QFont merged = styleFont.resolve(baseline);
merged.setResolveMask(baseline.resolveMask() | styleMask);
widget->setFont(merged);
}
void QStyleKitStylePrivate::refreshStyleFont(QWidget *widget)
{
if (!effectiveStyle() || !widget)
return;
const QWidget *targetWidget = containerWidget(widget);
QQStyleKitReader::ControlType controlType = controlTypeForWidget(targetWidget);
auto *shared = ensureSharedReader();
if (!shared)
return;
QStyleOption opt;
opt.initFrom(targetWidget);
const QQSK::State currentState = resolvedStateFor(controlType, opt.state, targetWidget);
shared->setControlTypeAndState(controlType, currentState, false);
setStyleFont(widget, shared->font());
}
void QStyleKitStylePrivate::unsetStylePalette(QWidget *widget)
{
auto it = customPaletteWidgets.find(widget);
if (it == customPaletteWidgets.end())
return;
auto customPalette = std::move(*it);
customPaletteWidgets.erase(it);
widget->setPalette(std::move(customPalette).reverted(widget->palette()));
}
void QStyleKitStylePrivate::setStylePalette(QWidget *widget, const QPalette &stylePalette) const
{
if (!effectiveStyle() || !widget)
return;
const quint64 styleMask = stylePalette.resolveMask();
if (styleMask == 0)
return;
if (!customPaletteWidgets.contains(widget)) {
customPaletteWidgets.insert(widget, { widget->palette(), 0 });
QObject::connect(widget, &QObject::destroyed, widget,
[this, widget]() { customPaletteWidgets.remove(widget); });
}
customPaletteWidgets[widget].resolveMask |= styleMask;
QPalette merged = stylePalette.resolve(widget->palette());
merged.setResolveMask(widget->palette().resolveMask() | styleMask);
widget->setPalette(merged);
}
// For widgets that partially or fully draw themselves (instead of delegating to the style),
// using palette roles, we need to push the style colors to those roles
void QStyleKitStylePrivate::refreshStylePalette(QWidget *widget)
{
if (!effectiveStyle() || !widget)
return;
const bool isWindow = widget->isWindow();
const bool isPaletteManaged = isSelfPaintingWidget(widget) || isWindow;
if (!isPaletteManaged)
return;
const QWidget *targetWidget = containerWidget(widget);
QQStyleKitReader::ControlType controlType = controlTypeForWidget(targetWidget);
auto *shared = ensureSharedReader();
if (!shared)
return;
QStyleOption opt;
opt.initFrom(targetWidget);
const QQSK::State currentState = resolvedStateFor(controlType, opt.state, targetWidget);
QPalette stylePalette;
shared->setControlTypeAndState(controlType, currentState, false);
if (isWindow) {
// Tooltips draw their text using QPalette::ToolTipText role
if (controlType == QQStyleKitReader::ControlType::ToolTip) {
if (const auto *text = shared->global()->text();
text && text->isDefined(QQSK::Property::Color)) {
stylePalette.setColor(QPalette::ToolTipText, text->color());
}
} else {
// Windows draw their own background using the QPalette::Window role
if (const auto *bg = shared->global()->background();
bg && bg->isDefined(QQSK::Property::Color)) {
stylePalette.setColor(QPalette::Window, bg->color());
}
shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled, false);
if (const auto *dbg = shared->global()->background();
dbg && dbg->isDefined(QQSK::Property::Color)) {
stylePalette.setColor(QPalette::Disabled, QPalette::Window, dbg->color());
}
}
} else {
// The remaining text-based widgets use the QPalette::Text/WindowText roles for their
// foreground color
shared->setControlTypeAndState(controlType, currentState, false);
if (const auto *text = shared->global()->text();
text && text->isDefined(QQSK::Property::Color)) {
stylePalette.setColor(QPalette::Text, text->color());
stylePalette.setColor(QPalette::WindowText, text->color());
}
shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled, false);
if (const auto *dt = shared->global()->text();
dt && dt->isDefined(QQSK::Property::Color)) {
stylePalette.setColor(QPalette::Disabled, QPalette::Text, dt->color());
stylePalette.setColor(QPalette::Disabled, QPalette::WindowText, dt->color());
}
}
setStylePalette(widget, stylePalette);
}
/*! \internal
Returns a reader for the given widget, creating and caching it if needed.
*/
QQStyleKitReader *QStyleKitStylePrivate::readerForWidget(const QWidget *widget) const
{
Q_Q(const QStyleKitStyle);
QQStyleKitStyle *effective = effectiveStyle();
if (!effective || !widget)
return nullptr;
if (auto it = widgetReaders.find(widget); it != widgetReaders.end())
return *it;
auto *widgetReader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
widgetReader->setExplicitStyle(effective);
widgetReader->setTarget(paintTarget(widget));
widgetReader->setControlType(controlTypeForWidget(widget));
widgetReader->setCompleted(true);
widgetReaders.insert(widget, widgetReader);
QObjectPrivate::connect(widget, &QObject::destroyed, this,
&QStyleKitStylePrivate::onWidgetDestroyed, Qt::UniqueConnection);
return widgetReader;
}
void QStyleKitStylePrivate::cleanupWidgetReader(const QWidget *widget) const
{
if (auto *reader = widgetReaders.take(widget))
reader->deleteLater();
cleanupSubElements(widget);
}
void QStyleKitStylePrivate::onWidgetDestroyed(QObject *w) const
{
cleanupWidgetReader(static_cast<QWidget *>(w));
}
/*! \internal
Returns the key identifying the sub-element (item-view cell, menu item or
menubar item) that \a opt refers to, or an invalid key if the option does
not identify one.
*/
QStyleKitStylePrivate::SubElementKey
QStyleKitStylePrivate::subElementKeyForOption(const QStyleOption *opt, const QWidget *widget)
{
SubElementKey key;
if (!widget)
return key;
#if QT_CONFIG(itemviews)
if (const auto *viewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
const QModelIndex &idx = viewOpt->index;
if (!idx.isValid())
return key;
key.widget = widget;
key.id = SubElementKey::ItemViewCell{ idx.model(), idx.internalId(), idx.row(),
idx.column() };
return key;
}
#endif
#if QT_CONFIG(menu)
if (const auto *menuOpt = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
if (menuOpt->menuItemType == QStyleOptionMenuItem::Separator)
return key;
QAction *action = nullptr;
if (const auto *menu = qobject_cast<const QMenu *>(widget))
action = menu->actionAt(menuOpt->rect.center());
else if (const auto *menuBar = qobject_cast<const QMenuBar *>(widget))
action = menuBar->actionAt(menuOpt->rect.center());
if (action) {
key.widget = widget;
key.id = SubElementKey::Action{ action };
}
return key;
}
#endif
#if QT_CONFIG(tabbar)
if (const auto *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
if (tabOpt->tabIndex < 0)
return key;
key.widget = widget;
key.id = SubElementKey::Tab{ tabOpt->tabIndex };
return key;
}
#endif
return key;
}
/*! \internal
Creates and returns a transitions-enabled reader for the given sub-element
that animates from \a fromState to \a toState
*/
QQStyleKitReader *QStyleKitStylePrivate::startSubElementTransition(
const SubElementKey &key, QQStyleKitReader::ControlType type, QQSK::State fromState,
QQSK::State toState, QQuickTransition *transition) const
{
Q_Q(const QStyleKitStyle);
QQStyleKitStyle *effective = effectiveStyle();
if (!effective)
return nullptr;
// Reclaim finished readers. running() is per (shared) transition object, so
// it turns false only once no instance of that transition animates anywhere
for (auto it = subElementAnimationReaders.begin(); it != subElementAnimationReaders.end();) {
const QQuickTransition *t = it.value()->transition();
if (!t || !t->running()) {
it.value()->deleteLater();
it = subElementAnimationReaders.erase(it);
} else {
++it;
}
}
if (subElementAnimationReaders.size() >= kMaxSubElementAnimationReaders) {
const auto it = subElementAnimationReaders.begin();
if (const QWidget *widget = it.key().widget)
paintTarget(widget)->update(); // snap to end state
it.value()->deleteLater();
subElementAnimationReaders.erase(it);
}
auto *reader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
reader->setExplicitStyle(effective);
reader->setTarget(paintTarget(key.widget));
// just stores the fromState, doesn't start the animation yet
reader->setControlTypeAndState(type, fromState);
reader->setCompleted(true);
// As delegates are created lazily, a fresh reader has none,
// so we need to create them now so delegates can already start animating changes
// Only create delegates actually used by subelements, ie: background, indicators
reader->background();
auto *indicator = reader->indicator();
indicator->first()->foreground();
indicator->second()->foreground();
// Start the transition to the new state
reader->setControlTypeAndState(type, toState);
if (!transition || !transition->running()) {
reader->deleteLater();
return nullptr;
}
subElementAnimationReaders.insert(key, reader);
return reader;
}
/*! \internal
Drops all sub-element state tracking and animation readers for \a widget.
*/
void QStyleKitStylePrivate::cleanupSubElements(const QWidget *widget) const
{
for (auto it = subElementStates.begin(); it != subElementStates.end();) {
if (it.key().widget == widget)
it = subElementStates.erase(it);
else
++it;
}
for (auto it = subElementAnimationReaders.begin(); it != subElementAnimationReaders.end();) {
if (it.key().widget == widget) {
it.value()->deleteLater();
it = subElementAnimationReaders.erase(it);
} else {
++it;
}
}
}
/*! \internal
Drops all sub-element state tracking and animation readers.
*/
void QStyleKitStylePrivate::clearAllSubElements() const
{
subElementStates.clear();
for (QQStyleKitReader *reader : std::as_const(subElementAnimationReaders))
reader->deleteLater();
subElementAnimationReaders.clear();
}
/*! \internal
Resolves the given QStyle::State into a QQSK::State based on the mapping of
state flags for the given control type.
*/
QQSK::State QStyleKitStylePrivate::resolvedStateFor(QQStyleKitReader::ControlType type,
QStyle::State state,
const QWidget *widget) const
{
QQSK::State flags;
flags.setFlag(QQSK::StateFlag::Hovered, (state & QStyle::State_MouseOver));
flags.setFlag(QQSK::StateFlag::Pressed, state & QStyle::State_Sunken);
flags.setFlag(QQSK::StateFlag::Checked, state & QStyle::State_On);
flags.setFlag(QQSK::StateFlag::Focused, state & QStyle::State_HasFocus);
flags.setFlag(QQSK::StateFlag::Highlighted, state & QStyle::State_Selected);
bool hasOrientation = false;
switch (type) {
case QQStyleKitReader::ProgressBar:
case QQStyleKitReader::ScrollBar:
case QQStyleKitReader::ScrollIndicator:
case QQStyleKitReader::Slider:
case QQStyleKitReader::ToolBar:
case QQStyleKitReader::ToolSeparator:
hasOrientation = true;
break;
default:
break;
}
if (hasOrientation)
flags.setFlag(QQSK::StateFlag::Vertical, !(state & QStyle::State_Horizontal));
// Some widgets don't set State_Enabled in their
// style option even when the widget is enabled.
// Fall back to widget->isEnabled() in that case
const bool enabled = (state & QStyle::State_Enabled) || (widget && widget->isEnabled());
flags.setFlag(QQSK::StateFlag::Disabled, !enabled);
// ComboBox uses QStyle::State_On to indicate the popup is open, which is
// not a "checked" semantic.
if (type == QQStyleKitReader::ControlType::ComboBox)
flags.setFlag(QQSK::StateFlag::Checked, false);
// Popup has no hover state in the Controls style
if (type == QQStyleKitReader::ControlType::Popup)
flags.setFlag(QQSK::StateFlag::Hovered, false);
// QTabBar sets State_Selected (not State_On) on the current tab
if (type == QQStyleKitReader::ControlType::TabButton) {
flags.setFlag(QQSK::StateFlag::Checked, state & QStyle::State_Selected);
flags.setFlag(QQSK::StateFlag::Highlighted, false);
}
return flags;
}
/*! \internal
Returns the ControlMetrics for the given control type and state,
reading from the style if needed and caching the result.
*/
const QStyleKitStylePrivate::ControlMetrics &
QStyleKitStylePrivate::metricsFor(QQStyleKitReader::ControlType type, QQSK::State state) const
{
if (auto it = metricsCache.find({ type, state }); it != metricsCache.end())
return *it;
auto *reader = ensureSharedReader();
Q_ASSERT(reader);
reader->setControlTypeAndState(type, state, false);
return *metricsCache.insert({ type, state }, metricsForReader(reader));
}
/*! \internal
Resolves the properties for the given widget, control type and state,
and returns them as a QQStyleKitResolved.
*/
QStyleKitStylePrivate::QQStyleKitResolved
QStyleKitStylePrivate::resolve(const QWidget *w, QQStyleKitReader::ControlType type,
QStyle::State state) const
{
QQStyleKitResolved out;
out.widget = w;
auto *reader = readerForWidget(w);
if (!reader)
return out;
const QQSK::State resolvedState = resolvedStateFor(type, state, w);
reader->setControlTypeAndState(type, resolvedState);
out.reader = reader;
out.metrics = &metricsFor(type, resolvedState);
return out;
}
/*! \internal
Resolves the properties for a sub-element of \a w (an item-view item, menu/
menuBar item), and returns them as a QQStyleKitResolved.
Sub-elements in a steady-state use the shared, transitions-disabled
subElementReader. When a sub-element is observed to change state and
the style defines a transition for the new state, a transient per-item
reader is created to run that transition
Pass \a track = false for synthetic resolves that should not register state changes
or retarget a running animation.
*/
QStyleKitStylePrivate::QQStyleKitResolved
QStyleKitStylePrivate::resolveSubElement(const QWidget *w, const QStyleOption *opt,
QQStyleKitReader::ControlType type, QStyle::State state,
bool track) const
{
QQStyleKitResolved out;
out.widget = w;
auto *steadyReader = ensureSubElementReader();
if (!steadyReader)
return out;
const QQSK::State newState = resolvedStateFor(type, state, w);
const SubElementKey key = subElementKeyForOption(opt, w);
QQStyleKitReader *reader = nullptr;
if (track && key.isValid()) {
QQStyleKitReader *animReader = subElementAnimationReaders.value(key);
const auto it = subElementStates.constFind(key);
const bool seen = it != subElementStates.cend();
// Fallback to a reasonable base state to transition from by stripping the
// interactive/pointer-driven flags from the new state.
constexpr QQSK::State InteractiveStates = QQSK::State(QQSK::StateFlag::Hovered)
| QQSK::StateFlag::Pressed | QQSK::StateFlag::Checked
| QQSK::StateFlag::Focused | QQSK::StateFlag::Highlighted;
const auto baseState = newState & ~InteractiveStates;
const QQSK::State prevState = seen ? *it : baseState;
if (prevState != newState) {
if (animReader) {
// State changed before transition finished
// Retarget to new state, the transition continues from the current interpolated
// values
animReader->setControlTypeAndState(type, newState);
} else {
// Only pointer-driven states should animate
// If an item first comes into view checked/highlighted, etc.,
// it should not animate
const bool shouldAnimate = seen || newState.testFlag(QQSK::StateFlag::Hovered)
|| newState.testFlag(QQSK::StateFlag::Pressed);
if (shouldAnimate) {
steadyReader->setControlTypeAndState(type, newState);
if (QQuickTransition *transition = steadyReader->transition())
animReader = startSubElementTransition(key, type, prevState,
newState, transition);
}
}
}
if (!seen || prevState != newState) {
QObjectPrivate::connect(w, &QObject::destroyed, this,
&QStyleKitStylePrivate::onWidgetDestroyed,
Qt::UniqueConnection);
if (subElementStates.size() >= kMaxSubElementStates)
subElementStates.clear();
subElementStates.insert(key, newState);
}
reader = animReader;
}
if (!reader)
reader = steadyReader;
reader->setControlTypeAndState(type, newState);
out.reader = reader;
out.metrics = &metricsFor(type, newState);
return out;
}
const QQStyleKitTextProperties *QStyleKitStylePrivate::QQStyleKitResolved::text() const
{
return reader ? reader->text() : nullptr;
}
const QQStyleKitDelegateProperties *QStyleKitStylePrivate::QQStyleKitResolved::background() const
{
return reader ? reader->background() : nullptr;
}
const QQStyleKitHandleProperties *QStyleKitStylePrivate::QQStyleKitResolved::handle() const
{
return reader ? reader->handle() : nullptr;
}
const QQStyleKitIndicatorWithSubTypes *QStyleKitStylePrivate::QQStyleKitResolved::indicator() const
{
return reader ? reader->indicator() : nullptr;
}
QFont QStyleKitStylePrivate::QQStyleKitResolved::font() const
{
return reader ? reader->font() : QFont();
}
QQStyleKitReader *QStyleKitStylePrivate::ensureSharedReader() const
{
if (sharedReader)
return sharedReader;
Q_Q(const QStyleKitStyle);
QQStyleKitStyle *effective = effectiveStyle();
if (!effective)
return nullptr;
sharedReader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
// Disable transitions since this reader is used for one-off metric reads in layout queries
sharedReader->setTransitionsEnabled(false);
sharedReader->setExplicitStyle(effective);
sharedReader->setCompleted(true);
return sharedReader;
}
QQStyleKitReader *QStyleKitStylePrivate::ensureSubElementReader() const
{
if (subElementReader)
return subElementReader;
Q_Q(const QStyleKitStyle);
QQStyleKitStyle *effective = effectiveStyle();
if (!effective)
return nullptr;
subElementReader = new QQStyleKitReader(const_cast<QStyleKitStyle *>(q));
// Disable transitions: this reader is retargeted across many sub-elements
// per paint pass and must never animate between their states
subElementReader->setTransitionsEnabled(false);
subElementReader->setExplicitStyle(effective);
subElementReader->setCompleted(true);
return subElementReader;
}
/*! \internal
Resolves the properties for the given control type and state using the shared reader,
which disables transitions and is used for layout queries, so that layout never depends
on animation state.
*/
QStyleKitStylePrivate::QQStyleKitLayoutResolved
QStyleKitStylePrivate::resolveLayout(QQStyleKitReader::ControlType type, QStyle::State state) const
{
QQStyleKitLayoutResolved out;
auto *reader = ensureSharedReader();
if (!reader)
return out;
const QQSK::State resolvedState = resolvedStateFor(type, state);
out.metrics = &metricsFor(type, resolvedState);
// metricsFor() sets the sharedReader to (type, resolvedState) on cache miss.
// On hit it didn't, so make sure staticProps / staticFont reflect the state
// and type of the caller
reader->setControlTypeAndState(type, resolvedState, false);
out.staticProps = reader->global();
out.staticFont = reader->font();
return out;
}
const QQStyleKitTextProperties *QStyleKitStylePrivate::QQStyleKitLayoutResolved::text() const
{
return staticProps ? staticProps->text() : nullptr;
}
const QQStyleKitDelegateProperties *
QStyleKitStylePrivate::QQStyleKitLayoutResolved::background() const
{
return staticProps ? staticProps->background() : nullptr;
}
const QQStyleKitHandleProperties *QStyleKitStylePrivate::QQStyleKitLayoutResolved::handle() const
{
return staticProps ? staticProps->handle() : nullptr;
}
const QQStyleKitIndicatorWithSubTypes *
QStyleKitStylePrivate::QQStyleKitLayoutResolved::indicator() const
{
return staticProps ? staticProps->indicator() : nullptr;
}
void QStyleKitStylePrivate::clearMetricsCache()
{
metricsCache.clear();
}
void QStyleKitStylePrivate::drawControlIndicator(const QQStyleKitDelegateProperties *indicator,
const QRectF &rect, QPainter *painter) const
{
if (!indicator || !indicator->visible() || indicator->opacity() <= 0 || !rect.isValid())
return;
// The foreground is a "child" of the indicator container so it
// inherits the indicator's transform. Apply it here and let the foreground stack its
// own on top.
QPainterStateGuard stateGuard(painter);
if (!applyDelegateTransform(painter, indicator, rect))
return;
// indicator (background)
QRectF indicatorRect = rect;
drawStyledItemContents(indicator, indicatorRect, painter);
const QQStyleKitDelegateProperties *foreground = nullptr;
if (auto *indicatorWithSubTypes =
qobject_cast<const QQStyleKitIndicatorWithSubTypes *>(indicator)) {
foreground = indicatorWithSubTypes->foreground();
} else if (auto *indicatorProps =
qobject_cast<const QQStyleKitIndicatorProperties *>(indicator)) {
foreground = indicatorProps->foreground();
} else {
return;
}
if (!foreground || !foreground->visible() || foreground->opacity() <= 0)
return;
// foreground
QRectF foregroundRect;
const uint foregroundAlign = resolvedAlignment(foreground->alignment(), Qt::AlignHCenter,
Qt::AlignVCenter);
const auto foregroundW = resolvedWidth(
foreground, indicatorRect.width() - foreground->leftMargin() - foreground->rightMargin());
const auto foregroundH = resolvedHeight(
foreground, indicatorRect.height() - foreground->topMargin() - foreground->bottomMargin());
foregroundRect.setSize(QSizeF(foregroundW, foregroundH));
if (foregroundAlign & Qt::AlignLeft)
foregroundRect.moveLeft(indicatorRect.left() + foreground->leftMargin());
else if (foregroundAlign & Qt::AlignHCenter)
foregroundRect.moveLeft(indicatorRect.left() + (indicatorRect.width() - foregroundW) / 2.0);
else if (foregroundAlign & Qt::AlignRight)
foregroundRect.moveLeft(indicatorRect.right() - foreground->rightMargin() - foregroundW);
if (foregroundAlign & Qt::AlignTop)
foregroundRect.moveTop(indicatorRect.top() + foreground->topMargin());
else if (foregroundAlign & Qt::AlignVCenter)
foregroundRect.moveTop(indicatorRect.top() + (indicatorRect.height() - foregroundH) / 2.0);
else if (foregroundAlign & Qt::AlignBottom)
foregroundRect.moveTop(indicatorRect.bottom() - foreground->bottomMargin() - foregroundH);
drawStyledItemRect(foreground, foregroundRect, painter);
}
void QStyleKitStylePrivate::drawControlText(const QQStyleKitTextProperties *textProps,
const QFont &font, const QRect &rect,
const QString &text, uint textFlags, QPainter *p,
Qt::Alignment defaultAlignment) const
{
uint flags = textFlags;
flags |= textProps
? resolvedAlignment(textProps->alignment(), defaultAlignment & Qt::AlignHorizontal_Mask,
defaultAlignment & Qt::AlignVertical_Mask)
: uint(defaultAlignment);
const QFont oldFont = p->font();
p->setFont(font.resolve(oldFont));
p->setBrush(Qt::NoBrush);
p->setPen(textProps ? textProps->color() : QColor());
p->drawText(rect, flags, text, nullptr);
}
void QStyleKitStylePrivate::drawStyledItemRect(const QQStyleKitDelegateProperties *props,
const QRectF &rect, QPainter *painter) const
{
if (!props || !props->visible() || props->opacity() <= 0 || !rect.isValid())
return;
QPainterStateGuard stateGuard(painter);
if (!applyDelegateTransform(painter, props, rect))
return;
drawStyledItemContents(props, rect, painter);
}
void QStyleKitStylePrivate::drawStyledItemContents(const QQStyleKitDelegateProperties *props,
const QRectF &rect, QPainter *painter) const
{
if (!props || !props->visible() || props->opacity() <= 0 || !rect.isValid())
return;
QPainterStateGuard stateGuard(painter);
painter->setRenderHint(QPainter::Antialiasing, true);
if (props->clip())
painter->setClipping(true);
// opacity
const auto opacity = props->opacity();
// border
const auto *border = props->border();
auto borderWidth = 0;
auto inset = 0.0;
QColor borderColor;
if (border) {
borderWidth = border->width();
borderColor = border->color();
inset = borderWidth / 2.0;
}
if (borderWidth > 0 && borderColor.isValid() && borderColor.alpha() > 0) {
QPen pen(borderColor, borderWidth);
painter->setPen(pen);
} else {
painter->setPen(Qt::NoPen);
}
// radius
// TODO: support different radius for each corner
const qreal minDimension = qMin(rect.width(), rect.height());
const qreal xRadius = qMax(0.0, qMin(props->topLeftRadius() - inset, minDimension / 2.0));
const qreal yRadius = qMax(0.0, qMin(props->bottomLeftRadius() - inset, minDimension / 2.0));
QRectF adjustedRect = rect.adjusted(inset, inset, -inset, -inset);
// gradients/color
// TODO: support palette roles for colors and gradients
QColor color = props->color();
QBrush colorBrush(color);
painter->setBrush(colorBrush);
if (color.isValid()) {
painter->setOpacity(opacity);
painter->drawRoundedRect(adjustedRect, xRadius, yRadius, Qt::AbsoluteSize);
}
if (QQuickGradient *gradient = props->gradient()) {
QLinearGradient linearGradient(rect.topLeft(), rect.bottomLeft());
QQmlListProperty<QQuickGradientStop> stops = gradient->stops();
const int stopCount = stops.count(&stops);
for (int i = 0; i < stopCount; i++) {
QQuickGradientStop *stop = static_cast<QQuickGradientStop *>(stops.at(&stops, i));
linearGradient.setColorAt(stop->position(), stop->color());
}
QBrush gradientBrush(linearGradient);
painter->setBrush(gradientBrush);
painter->setOpacity(opacity);
painter->drawRoundedRect(adjustedRect, xRadius, yRadius, Qt::AbsoluteSize);
}
// image
drawStyledItemImage(props->image(), adjustedRect, opacity, painter);
}
void QStyleKitStylePrivate::drawStyledItemImage(const QQStyleKitImageProperties *image,
const QRectF &rect, qreal opacity,
QPainter *painter) const
{
if (!image || image->source().isEmpty() || image->color().alpha() <= 0)
return;
QString imageSource = image->source().toString();
if (imageSource.startsWith(QLatin1String("qrc:/")))
imageSource = imageSource.mid(3);
QUrl imageUrl(imageSource);
QString imagePath = imageUrl.isLocalFile() ? imageUrl.toLocalFile() : imageSource;
QPixmap pixmap(imagePath);
if (pixmap.isNull())
return;
if (image->color().isValid()) {
QImage coloredImage = pixmap.toImage();
QPainter imagePainter(&coloredImage);
imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
imagePainter.fillRect(coloredImage.rect(), image->color());
imagePainter.end();
pixmap = QPixmap::fromImage(coloredImage);
}
painter->setOpacity(opacity);
const QSizeF pixmapSize = pixmap.deviceIndependentSize();
switch (image->fillMode()) {
case QQuickImage::PreserveAspectFit: {
const QSize scaled = pixmapSize.scaled(rect.size(), Qt::KeepAspectRatio).toSize();
const QPointF topLeft(rect.x() + (rect.width() - scaled.width()) / 2.0,
rect.y() + (rect.height() - scaled.height()) / 2.0);
painter->drawPixmap(QRectF(topLeft, scaled), pixmap, QRectF(QPointF(0, 0), pixmapSize));
break;
}
case QQuickImage::PreserveAspectCrop: {
const QSize scaled = pixmapSize
.scaled(rect.size(), Qt::KeepAspectRatioByExpanding)
.toSize();
const QPointF topLeft(rect.x() + (rect.width() - scaled.width()) / 2.0,
rect.y() + (rect.height() - scaled.height()) / 2.0);
QPainterStateGuard stateGuard(painter);
painter->setClipRect(rect);
painter->drawPixmap(QRectF(topLeft, scaled), pixmap, QRectF(QPointF(0, 0), pixmapSize));
break;
}
// TODO: Support remaining fill modes
case QQuickImage::Stretch:
default:
painter->drawPixmap(rect, pixmap, QRectF(QPointF(0, 0), pixmapSize));
break;
}
}
QRect QStyleKitStylePrivate::getAlignedRectInContainer(const QRect &container, const QSize &size,
uint alignment, const QMargins &padding,
const QMargins &margins) const
{
QRect r(QPoint(0, 0), size);
if (alignment & Qt::AlignLeft)
r.moveLeft(container.x() + padding.left() + margins.left());
else if (alignment & Qt::AlignHCenter)
r.moveLeft(container.x() + (container.width() - size.width()) / 2);
else // AlignRight
r.moveLeft(container.right() + 1 - padding.right() - margins.right() - size.width());
if (alignment & Qt::AlignTop)
r.moveTop(container.y() + padding.top() + margins.top());
else if (alignment & Qt::AlignVCenter)
r.moveTop(container.y() + (container.height() - size.height()) / 2);
else // AlignBottom
r.moveTop(container.bottom() + 1 - padding.bottom() - margins.bottom() - size.height());
return r;
}
QStyleKitStylePrivate::ControlMetrics
QStyleKitStylePrivate::metricsForReader(QQStyleKitReader *reader) const
{
Q_ASSERT(reader);
using QtPrivate::qSaturateRound;
const QQStyleKitControlProperties *props = reader->global();
ControlMetrics metrics;
metrics.bgImplicitSize = QSize(0, 0);
metrics.textPadding = QMargins(0, 0, 0, 0);
metrics.padding = QMargins(qSaturateRound(props->leftPadding()),
qSaturateRound(props->topPadding()),
qSaturateRound(props->rightPadding()),
qSaturateRound(props->bottomPadding()));
metrics.spacing = qSaturateRound(props->spacing());
metrics.margins = QMargins(0, 0, 0, 0);
metrics.indicatorImplicitSize = QSize(0, 0);
metrics.indicatorMargins = QMargins(0, 0, 0, 0);
const auto *background = props->background();
// Note: scale is deliberately absent here as it should not affect layout
const auto elementSize = [](qreal w, qreal h) {
constexpr qreal zero(.0);
return QSizeF(std::max(zero, w), std::max(zero, h)).toSize();
};
if (background) {
metrics.bgImplicitSize = elementSize(background->width(), background->height());
metrics.margins = elementMargins(background);
}
const auto *textProps = props->text();
if (textProps) {
metrics.textPadding = QMargins(qSaturateRound(textProps->leftPadding()),
qSaturateRound(textProps->topPadding()),
qSaturateRound(textProps->rightPadding()),
qSaturateRound(textProps->bottomPadding()));
}
const auto *indicator = props->indicator();
if (indicator) {
metrics.indicatorMargins = elementMargins(indicator);
metrics.indicatorImplicitSize = elementSize(indicator->width(), indicator->height());
}
const auto *handle = props->handle();
if (handle) {
metrics.handleImplicitSize = elementSize(handle->width(), handle->height());
metrics.handleMargins = elementMargins(handle);
}
return metrics;
}
/*!
Constructs a QStyleKitStyle with no style loaded.
Use \l setStylePath() to load a QML \l Style after construction. Until a
Style loads successfully, the widgets use an empty fallback style.
*/
QStyleKitStyle::QStyleKitStyle()
: QCommonStyle(*new QStyleKitStylePrivate())
{
}
/*!
Constructs a QStyleKitStyle and loads the QML \l Style at \a filePath.
See the \l stylePath property for the accepted path forms.
If the path is invalid or the root object of the loaded component is not
a \l Style, QStyleKitStyle emits a warning and the widgets use an empty
fallback style until a valid \l stylePath is set.
*/
QStyleKitStyle::QStyleKitStyle(const QString &filePath)
: QCommonStyle(*new QStyleKitStylePrivate())
{
Q_D(QStyleKitStyle);
d->stylePath = filePath;
if (!d->loadStyle()) {
d->stylePath.clear();
qWarning("QStyleKitStyle: Failed to load style from %s", qPrintable(filePath));
}
}
/*!
Destroys the QStyleKitStyle.
*/
QStyleKitStyle::~QStyleKitStyle()
{
}
/*!
Returns the path most recently set for the QML \l Style file.
\sa setStylePath()
*/
QString QStyleKitStyle::stylePath() const
{
Q_D(const QStyleKitStyle);
return d->stylePath;
}
/*!
Loads the QML \l Style at \a filePath and applies it to all widgets.
\a filePath is a path to a local file or a path to a file in the resource
file system; see the \l stylePath property for the accepted forms. If it is
the same as the current \l stylePath, this function does nothing.
If the new style does not load, QStyleKitStyle emits a warning, keeps
the previously loaded style active, and does not emit \l stylePathChanged().
\sa stylePath()
*/
void QStyleKitStyle::setStylePath(const QString &filePath)
{
Q_D(QStyleKitStyle);
if (d->stylePath == filePath)
return;
const QString oldThemeName = themeName();
const QStringList oldThemeNames = availableThemeNames();
const QStringList oldCustomThemeNames = customThemeNames();
const QString previousPath = d->stylePath;
d->stylePath = filePath;
if (!d->loadStyle()) {
d->stylePath = previousPath;
return;
}
d->updateStyle();
if (const QStringList names = availableThemeNames(); names != oldThemeNames)
emit availableThemeNamesChanged(names);
if (const QStringList names = customThemeNames(); names != oldCustomThemeNames)
emit customThemeNamesChanged(names);
emit stylePathChanged(d->stylePath);
}
/*!
Returns the name of the requested theme, or an empty string when no
\l Style is loaded.
When the theme name is \c System, this function returns \c System rather
than the \c Light or \c Dark theme that the platform color scheme
resolves to.
\sa setThemeName(), availableThemeNames()
*/
QString QStyleKitStyle::themeName() const
{
Q_D(const QStyleKitStyle);
return d->style ? d->style->themeName() : QString();
}
/*!
Activates the theme named \a themeName.
\a themeName must be one of the entries in \l availableThemeNames(),
which includes the special name \c System for following the platform
color scheme. The comparison ignores case.
If no \l Style is loaded, this function emits a warning and returns
without changing the active theme. If a Style is loaded but \a themeName
matches none of its themes, the Style emits a warning and applies no
theme.
\sa themeName(), availableThemeNames()
*/
void QStyleKitStyle::setThemeName(const QString &themeName)
{
Q_D(QStyleKitStyle);
if (!d->style) {
qWarning("QStyleKitStyle: No style loaded, cannot set theme name.");
return;
}
if (d->style->themeName() == themeName)
return;
d->style->setThemeName(themeName);
d->updateStyle();
emit themeNameChanged(d->style->themeName());
}
/*!
Returns the names of all themes that the loaded \l Style exposes. The
list always includes \c System, and includes \c Light and \c Dark when
the Style defines those themes, together with any custom themes that
the style author defines. Returns an empty list when no style is
loaded.
\sa customThemeNames(), themeName()
*/
QStringList QStyleKitStyle::availableThemeNames() const
{
Q_D(const QStyleKitStyle);
return d->style ? d->style->availableThemeNames() : QStringList();
}
/*!
Returns the names of the custom themes defined by the loaded
\l Style, excluding the built-in \c Light and \c Dark themes.
Returns an empty list when no style is loaded.
\sa availableThemeNames()
*/
QStringList QStyleKitStyle::customThemeNames() const
{
Q_D(const QStyleKitStyle);
return d->style ? d->style->customThemeNames() : QStringList();
}
/*! \reimp */
void QStyleKitStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
const QWidget *w) const
{
Q_D(const QStyleKitStyle);
switch (pe) {
#if QT_CONFIG(tooltip)
case PE_PanelTipLabel: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolTip, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
return;
}
#endif
#if QT_CONFIG(menu)
case PE_PanelMenu: {
const auto controlType = w && w->inherits("QComboBoxPrivateContainer")
? QQStyleKitReader::ControlType::Popup
: QQStyleKitReader::ControlType::Menu;
const auto r = d->resolve(w, controlType, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
return;
}
case PE_FrameMenu:
// already drawn in PE_PanelMenu
return;
#endif // QT_CONFIG(menu)
#if QT_CONFIG(menubar)
case PE_PanelMenuBar: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::MenuBar, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
return;
}
#endif // QT_CONFIG(menubar)
case PE_FrameButtonBevel:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
const auto controlType = btn->features & QStyleOptionButton::Flat
? QQStyleKitReader::ControlType::FlatButton
: QQStyleKitReader::ControlType::Button;
const auto r = d->resolve(w, controlType, btn->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect, p);
return;
}
break;
case PE_Frame: {
QQStyleKitReader::ControlType controlType;
const bool isPopup = w
&& (false
#if QT_CONFIG(combobox)
|| w->inherits("QComboBoxPrivateContainer")
#endif
);
if (isPopup)
controlType = QQStyleKitReader::ControlType::Popup;
#if QT_CONFIG(lineedit)
else if (qobject_cast<const QLineEdit *>(w))
controlType = QQStyleKitReader::ControlType::TextField;
#endif
else
controlType = QQStyleKitReader::ControlType::Frame;
const auto r = d->resolve(w, controlType, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect, p);
return;
}
#if QT_CONFIG(lineedit)
case PE_PanelLineEdit:
if (const auto *lineEdit = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
// LineEdit sets Sunken flag to indicate Sunken frame,
// but the style uses it to indicate pressed state, so ignore it
QStyleOption lineEditOpt(*lineEdit);
lineEditOpt.state &= ~QStyle::State_Sunken;
const auto r = d->resolve(w, QQStyleKitReader::ControlType::TextField,
lineEditOpt.state);
if (!r.isValid())
break;
// LineEdit draws its own text using the Text role so update that role in the palette
if (auto *le = qobject_cast<const QLineEdit *>(w)) {
if (const auto *txt = r.text(); txt && txt->isDefined(QQSK::Property::Color)) {
QPalette stylePalette;
stylePalette.setColor(QPalette::Text, txt->color());
d->setStylePalette(const_cast<QLineEdit *>(le), stylePalette);
}
const_cast<QStyleKitStylePrivate *>(d)->setStyleFont(const_cast<QLineEdit *>(le),
r.font());
}
const QObject *parent = w ? w->parent() : nullptr;
# if QT_CONFIG(spinbox)
const bool isInSpinBox = qobject_cast<const QAbstractSpinBox *>(parent);
# else
const bool isInSpinBox = false;
# endif
# if QT_CONFIG(combobox)
const bool isInComboBox = qobject_cast<const QComboBox *>(parent);
# else
const bool isInComboBox = false;
# endif
// For spinbox and combobox, the line edit doesn't have its own background in the Controls style
if (isInSpinBox || isInComboBox)
return;
d->drawStyledItemRect(r.background(), opt->rect, p);
return;
}
break;
#endif // QT_CONFIG(lineedit)
#if QT_CONFIG(itemviews)
case PE_PanelItemViewItem: {
const auto r = d->resolveSubElement(w, opt, itemViewControlType(opt), opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect, p);
return;
}
#endif // QT_CONFIG(itemviews)
case PE_IndicatorCheckBox:
case PE_IndicatorRadioButton: {
const auto controlType = pe == PE_IndicatorCheckBox
? QQStyleKitReader::ControlType::CheckBox
: QQStyleKitReader::ControlType::RadioButton;
const auto r = d->resolve(w, controlType, opt->state);
if (!r.isValid())
break;
d->drawControlIndicator(r.indicator(), opt->rect, p);
return;
}
case PE_IndicatorArrowDown: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ComboBox, opt->state);
if (!r.isValid())
break;
d->drawControlIndicator(r.indicator(), opt->rect, p);
return;
}
case PE_IndicatorItemViewItemCheck: {
// track = false: Checked state is synthetic
const auto r = d->resolveSubElement(w, opt, itemViewControlType(opt), opt->state, false);
if (!r.isValid())
break;
d->drawControlIndicator(r.indicator(), opt->rect, p);
return;
}
#if QT_CONFIG(spinbox)
case PE_IndicatorSpinUp:
case PE_IndicatorSpinDown: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::SpinBox, opt->state);
if (!r.isValid())
break;
const auto *indicator = r.indicator();
const auto *upDownIndicator = indicator
? (pe == PE_IndicatorSpinUp ? indicator->first() : indicator->second())
: nullptr;
d->drawControlIndicator(upDownIndicator, opt->rect, p);
return;
}
#endif // QT_CONFIG(spinbox)
#if QT_CONFIG(groupbox)
case PE_FrameGroupBox: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::GroupBox, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect, p);
return;
}
#endif // QT_CONFIG(groupbox)
#if QT_CONFIG(tabwidget)
case PE_FrameTabWidget: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::Page, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect, p);
return;
}
#endif // QT_CONFIG(tabwidget)
#if QT_CONFIG(tabbar)
// Not (yet) supported in StyleKit
case PE_IndicatorTabClose:
case PE_IndicatorTabTearRight:
case PE_IndicatorTabTearLeft:
return;
#endif // QT_CONFIG(tabbar)
#if QT_CONFIG(toolbutton)
case PE_FrameButtonTool:
case PE_PanelButtonTool: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolButton, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect, p);
return;
}
#endif // QT_CONFIG(toolbutton)
#if QT_CONFIG(toolbar)
case PE_IndicatorToolBarHandle:
return; // no handle in Controls style
case PE_IndicatorToolBarSeparator: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolSeparator, opt->state);
if (!r.isValid())
break;
const auto &metrics = *r.metrics;
// background
QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
d->drawStyledItemRect(r.background(), frameRect, p);
// indicator
const auto *indicator = r.indicator();
if (!indicator || !indicator->visible() || indicator->opacity() == 0)
return;
QRect contentRect = frameRect.marginsRemoved(metrics.padding);
const int iw = QtPrivate::qSaturateRound(
resolvedWidth(indicator,
contentRect.width()
- metrics.indicatorMargins.left()
- metrics.indicatorMargins.right()));
const int ih = QtPrivate::qSaturateRound(
resolvedHeight(indicator,
contentRect.height()
- metrics.indicatorMargins.top()
- metrics.indicatorMargins.bottom()));
const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignHCenter,
Qt::AlignVCenter);
const QRect indicatorRect = d->getAlignedRectInContainer(
contentRect, QSize(iw, ih), alignment, QMargins(), metrics.indicatorMargins);
d->drawControlIndicator(indicator, visualRect(opt->direction, contentRect, indicatorRect),
p);
return;
}
#endif
default:
break;
}
QCommonStyle::drawPrimitive(pe, opt, p, w);
}
/*! \reimp */
void QStyleKitStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
const QWidget *w) const
{
Q_D(const QStyleKitStyle);
switch (element) {
case CE_PushButton:
if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
proxy()->drawControl(CE_PushButtonBevel, btn, p, w);
QStyleOptionButton btnContent(*btn);
btnContent.rect = subElementRect(SE_PushButtonContents, btn, w);
proxy()->drawControl(CE_PushButtonLabel, &btnContent, p, w);
}
return;
case CE_PushButtonBevel:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
QStyleOptionButton btnBg(*btn);
btnBg.rect = subElementRect(SE_PushButtonBevel, btn, w);
proxy()->drawPrimitive(PE_FrameButtonBevel, &btnBg, p, w);
}
return;
case CE_PushButtonLabel:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
const auto controlType = btn->features & QStyleOptionButton::Flat
? QQStyleKitReader::ControlType::FlatButton
: QQStyleKitReader::ControlType::Button;
const auto r = d->resolve(w, controlType, btn->state);
if (!r.isValid())
break;
const auto metrics = r.metrics;
QRect textRect = opt->rect;
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
// icon
const QIcon icon = btn->icon;
if (!icon.isNull()) {
const auto *textProps = r.text();
uint iconTextFlags = textFlags;
iconTextFlags |= textProps
? resolvedAlignment(textProps->alignment(), Qt::AlignHCenter, Qt::AlignVCenter)
: uint(Qt::AlignHCenter | Qt::AlignVCenter);
QIcon::Mode mode = btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
if (mode == QIcon::Normal && btn->state & State_HasFocus)
mode = QIcon::Active;
QIcon::State iconState = btn->state & State_On ? QIcon::On : QIcon::Off;
const auto paintDeviceDpr = p->device()->devicePixelRatio();
QPixmap pixmap = icon.pixmap(btn->iconSize, paintDeviceDpr, mode, iconState);
int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
int labelWidth = pixmapWidth;
int iconSpacing = metrics->spacing;
int textWidth = btn->fontMetrics
.boundingRect(opt->rect, iconTextFlags, btn->text)
.width();
if (!btn->text.isEmpty()) {
labelWidth += (textWidth
+ iconSpacing + textProps->leftPadding() + textProps->rightPadding());
}
QRect iconRect;
if (iconTextFlags & Qt::AlignLeft) {
iconRect = QRect(textRect.x(),
textRect.y() + (textRect.height() - pixmapHeight) / 2,
pixmapWidth, pixmapHeight);
} else if (iconTextFlags & Qt::AlignHCenter) {
iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2,
textRect.y() + (textRect.height() - pixmapHeight) / 2,
pixmapWidth, pixmapHeight);
} else {
iconRect = QRect(textRect.x() + textRect.width() - labelWidth,
textRect.y() + (textRect.height() - pixmapHeight) / 2,
pixmapWidth, pixmapHeight);
}
iconRect = visualRect(btn->direction, textRect, iconRect);
// After placing the icon, left-align the text relative to it
textFlags &= ~Qt::AlignHorizontal_Mask;
textFlags |= Qt::AlignLeft;
if (btn->direction == Qt::RightToLeft)
textRect.setRight(iconRect.left() - iconSpacing);
else
textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing);
p->drawPixmap(iconRect, pixmap);
}
if (btn->features & QStyleOptionButton::HasMenu) {
int indicatorSize = pixelMetric(PM_MenuButtonIndicator, btn, w);
if (btn->direction == Qt::LeftToRight)
textRect = textRect.adjusted(0, 0, -indicatorSize, 0);
else
textRect = textRect.adjusted(indicatorSize, 0, 0, 0);
}
d->drawControlText(r.text(), r.font(), textRect, btn->text, textFlags, p);
return;
}
break;
case CE_CheckBox:
case CE_RadioButton:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
const auto controlType = element == CE_CheckBox
? QQStyleKitReader::ControlType::CheckBox
: QQStyleKitReader::ControlType::RadioButton;
const auto r = d->resolve(w, controlType, btn->state);
if (!r.isValid())
break;
QRect backgroundRect = btn->rect.marginsRemoved(r.metrics->margins);
// background
d->drawStyledItemRect(r.background(), backgroundRect, p);
// label
QStyleOptionButton btnContent(*btn);
const auto contentElement = element == CE_CheckBox
? SE_CheckBoxContents
: SE_RadioButtonContents;
btnContent.rect = subElementRect(contentElement, btn, w);
const auto labelElement = element == CE_CheckBox
? CE_CheckBoxLabel
: CE_RadioButtonLabel;
proxy()->drawControl(labelElement, &btnContent, p, w);
// indicator
QStyleOptionButton indicator(*btn);
const auto indicatorElement = element == CE_CheckBox
? SE_CheckBoxIndicator
: SE_RadioButtonIndicator;
indicator.rect = subElementRect(indicatorElement, btn, w);
const auto primitiveElement = element == CE_CheckBox
? PE_IndicatorCheckBox
: PE_IndicatorRadioButton;
proxy()->drawPrimitive(primitiveElement, &indicator, p, w);
return;
}
break;
case CE_CheckBoxLabel:
case CE_RadioButtonLabel:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
const auto controlType = element == CE_CheckBoxLabel
? QQStyleKitReader::ControlType::CheckBox
: QQStyleKitReader::ControlType::RadioButton;
const auto r = d->resolve(w, controlType, btn->state);
if (!r.isValid())
break;
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
d->drawControlText(r.text(), r.font(), opt->rect, btn->text, textFlags, p);
return;
}
break;
#if QT_CONFIG(combobox)
case CE_ComboBoxLabel:
if (const auto *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
if (comboBox->editable)
return;
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ComboBox, comboBox->state);
if (!r.isValid())
break;
const QRect textRect = subControlRect(CC_ComboBox, comboBox, SC_ComboBoxEditField, w)
.marginsRemoved(r.metrics->textPadding);
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
d->drawControlText(r.text(), r.font(), textRect, comboBox->currentText, textFlags, p);
return;
}
break;
#endif // QT_CONFIG(combobox)
#if QT_CONFIG(progressbar)
case CE_ProgressBar:
if (const QStyleOptionProgressBar *progressBar =
qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ProgressBar,
progressBar->state);
if (!r.isValid())
break;
QRect backgroundRect = progressBar->rect.marginsRemoved(r.metrics->margins);
// background
d->drawStyledItemRect(r.background(), backgroundRect, p);
// groove
QStyleOptionProgressBar contents(*progressBar);
contents.rect = subElementRect(SE_ProgressBarGroove, progressBar, w);
const QRect grooveRect = contents.rect;
proxy()->drawControl(CE_ProgressBarGroove, &contents, p, w);
// track
contents.rect = subElementRect(SE_ProgressBarContents, progressBar, w);
// The track is the groove's foreground child, so it inherits the indicator's
// transform
QPainterStateGuard stateGuard(p);
if (applyDelegateTransform(p, r.indicator(), grooveRect))
proxy()->drawControl(CE_ProgressBarContents, &contents, p, w);
// We intentionally don't draw the label as it is not drawn on the Controls Style
return;
}
break;
case CE_ProgressBarGroove: {
// groove = indicator background
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ProgressBar, opt->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.indicator(), opt->rect, p);
return;
}
case CE_ProgressBarContents:
// contents = indicator foreground
if (const auto *progressBar = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ProgressBar,
progressBar->state);
if (!r.isValid())
break;
const auto progress = progressBar->progress;
const auto ratio = progressBar->maximum > progressBar->minimum
? (progress - progressBar->minimum)
/ static_cast<qreal>(progressBar->maximum - progressBar->minimum)
: 0;
const auto width = static_cast<int>(progressBar->rect.width() * ratio);
const auto x = progressBar->invertedAppearance
? progressBar->rect.right() - width
: progressBar->rect.left();
const auto contentsRect = QRect(x, progressBar->rect.y(),
width, progressBar->rect.height());
const auto *foreground = r.indicator() ? r.indicator()->foreground() : nullptr;
if (foreground && foreground->visible() && foreground->opacity() > 0) {
QPainterStateGuard stateGuard(p);
if (applyDelegateTransform(p, foreground, progressBar->rect))
d->drawStyledItemContents(foreground, contentsRect, p);
}
return;
}
break;
#endif // QT_CONFIG(progressbar)
#if QT_CONFIG(itemviews)
case CE_ItemViewItem:
if (const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
const auto r = d->resolveSubElement(w, opt, itemViewControlType(opt), opt->state);
if (!r.isValid())
break;
QStyleOptionViewItem optBg(*itemViewOption);
optBg.rect = optBg.rect.marginsRemoved(r.metrics->margins);
QRect indicatorRect = subElementRect(SE_ItemViewItemCheckIndicator, itemViewOption, w);
QRect iconRect = subElementRect(SE_ItemViewItemDecoration, itemViewOption, w);
QRect textRect = subElementRect(SE_ItemViewItemText, itemViewOption, w);
// Capture text properties + font now, before the indicator
// drawPrimitive below potentially mutates the reader's state
const auto *itemTextProps = r.text();
const QFont itemFont = r.font();
// background
proxy()->drawPrimitive(PE_PanelItemViewItem, &optBg, p, w);
// indicator
if (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator) {
QStyleOptionViewItem option(*itemViewOption);
option.rect = indicatorRect;
option.state = option.state & ~QStyle::State_HasFocus;
switch (itemViewOption->checkState) {
case Qt::Unchecked:
option.state |= QStyle::State_Off;
break;
case Qt::PartiallyChecked:
option.state |= QStyle::State_NoChange;
break;
case Qt::Checked:
option.state |= QStyle::State_On;
break;
}
proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, p, w);
}
// icon
QIcon::Mode mode = QIcon::Normal;
if (!(itemViewOption->state & QStyle::State_Enabled))
mode = QIcon::Disabled;
else if (itemViewOption->state & QStyle::State_Selected)
mode = QIcon::Selected;
QIcon::State state = itemViewOption->state & QStyle::State_Open
? QIcon::On
: QIcon::Off;
itemViewOption->icon.paint(p, iconRect, itemViewOption->decorationAlignment, mode,
state);
// draw text
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
d->drawControlText(itemTextProps, itemFont, textRect, itemViewOption->text, textFlags,
p, Qt::AlignLeft | Qt::AlignVCenter);
return;
}
break;
#endif // QT_CONFIG(itemviews)
#ifndef QT_NO_FRAME
case CE_ShapedFrame: {
# if QT_CONFIG(scrollarea)
if (qobject_cast<const QAbstractScrollArea *>(w))
// ScrollView has no styling in Controls, keep consistent
return;
# endif
auto controlType = QQStyleKitReader::ControlType::Frame;
# if QT_CONFIG(combobox)
if (w && w->inherits("QComboBoxPrivateContainer"))
controlType = QQStyleKitReader::ControlType::Popup;
# endif
# if QT_CONFIG(textedit)
else if (w && qobject_cast<const QTextEdit *>(w))
controlType = QQStyleKitReader::ControlType::TextArea;
# endif
# if QT_CONFIG(label)
else if (w && qobject_cast<const QLabel *>(w))
controlType = QQStyleKitReader::ControlType::Label;
# endif
// Fallback to Frame
if (qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
const auto r = d->resolve(w, controlType, opt->state);
if (!r.isValid())
break;
QRect backgroundRect = opt->rect.marginsRemoved(r.metrics->margins);
d->drawStyledItemRect(r.background(), backgroundRect, p);
return;
}
break;
}
#endif // QT_NO_FRAME
#if QT_CONFIG(scrollbar)
case CE_ScrollBarSlider: {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ScrollBar, opt->state);
if (!r.isValid())
break;
d->drawControlIndicator(r.indicator(), opt->rect, p);
return;
}
#endif // QT_CONFIG(scrollbar)
#if QT_CONFIG(toolbutton)
case CE_ToolButtonLabel:
if (const auto *tool = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolButton, tool->state);
if (!r.isValid())
break;
const auto *metrics = r.metrics;
QRect rect = tool->rect.marginsRemoved(metrics->textPadding);
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
const bool hasArrow = tool->features & QStyleOptionToolButton::Arrow;
if ((!hasArrow && tool->icon.isNull() && !tool->text.isEmpty())
|| tool->toolButtonStyle == Qt::ToolButtonTextOnly) {
d->drawControlText(r.text(), r.font(), rect, tool->text, textFlags, p);
return;
}
QPixmap pm;
QSize pmSize;
if (!hasArrow && !tool->icon.isNull()) {
const QIcon::State iconState = tool->state & State_On ? QIcon::On : QIcon::Off;
QIcon::Mode mode;
if (!(tool->state & State_Enabled))
mode = QIcon::Disabled;
else if ((tool->state & State_MouseOver) && (tool->state & State_AutoRaise))
mode = QIcon::Active;
else
mode = QIcon::Normal;
pm = tool->icon.pixmap(tool->iconSize, p->device()->devicePixelRatio(), mode,
iconState);
pmSize = pm.size() / pm.devicePixelRatio();
}
if (tool->toolButtonStyle == Qt::ToolButtonIconOnly || tool->text.isEmpty()) {
if (!pm.isNull()) {
p->drawPixmap(visualRect(tool->direction, rect,
QRect(rect.x() + (rect.width() - pmSize.width()) / 2,
rect.y() + (rect.height() - pmSize.height()) / 2,
pmSize.width(), pmSize.height())),
pm);
}
return;
}
if (tool->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
if (!pm.isNull()) {
const QRect pr(rect.x() + (rect.width() - pmSize.width()) / 2, rect.y(),
pmSize.width(), pmSize.height());
p->drawPixmap(visualRect(tool->direction, rect, pr), pm);
QRect tr = rect;
tr.setTop(rect.y() + pmSize.height() + metrics->spacing);
d->drawControlText(r.text(), r.font(), visualRect(tool->direction, rect, tr),
tool->text, textFlags, p, Qt::AlignCenter);
} else {
d->drawControlText(r.text(), r.font(), rect, tool->text, textFlags, p,
Qt::AlignCenter);
}
} else {
if (!pm.isNull()) {
const QRect pr(rect.x(), rect.y() + (rect.height() - pmSize.height()) / 2,
pmSize.width(), pmSize.height());
p->drawPixmap(visualRect(tool->direction, rect, pr), pm);
QRect tr = rect;
tr.setLeft(rect.x() + pmSize.width() + metrics->spacing);
d->drawControlText(r.text(), r.font(), visualRect(tool->direction, rect, tr),
tool->text, textFlags, p, Qt::AlignLeft | Qt::AlignVCenter);
} else {
d->drawControlText(r.text(), r.font(), rect, tool->text, textFlags, p,
Qt::AlignLeft | Qt::AlignVCenter);
}
}
return;
}
break;
#endif // QT_CONFIG(toolbutton)
#if QT_CONFIG(menu)
case CE_MenuItem:
if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
using QtPrivate::qSaturateRound;
// MenuItemSeperator
if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::MenuSeparator,
menuItem->state);
if (!r.isValid())
break;
const auto metrics = r.metrics;
QRect contentRect = opt->rect
.marginsRemoved(metrics->margins)
.marginsRemoved(metrics->padding);
const auto *indicator = r.indicator();
if (indicator && indicator->visible() && indicator->opacity() > 0) {
const int w = qSaturateRound(resolvedWidth(
indicator,
contentRect.width() - indicator->leftMargin() - indicator->rightMargin()));
const int h = qSaturateRound(resolvedHeight(
indicator,
contentRect.height() - indicator->topMargin() - indicator->bottomMargin()));
const uint align = resolvedAlignment(indicator->alignment(), Qt::AlignHCenter,
Qt::AlignVCenter);
const QRect indicatorRect = d->getAlignedRectInContainer(
contentRect, QSize(w, h), align, QMargins(), elementMargins(indicator));
d->drawControlIndicator(
indicator, visualRect(opt->direction, contentRect, indicatorRect), p);
}
return;
}
// MenuItem
QStyle::State adjustedState = menuItem->state;
if (adjustedState & State_Selected)
adjustedState |= State_MouseOver;
const auto r = d->resolveSubElement(w, opt, QQStyleKitReader::ControlType::MenuItem,
adjustedState);
if (!r.isValid())
break;
// background
d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
// indicators
const QRect contentRect = opt->rect.marginsRemoved(r.metrics->padding);
const auto *indicator = r.indicator();
const auto *first = indicator ? indicator->first() : nullptr;
const auto *second = indicator ? indicator->second() : nullptr;
int leftOffset = 0, rightOffset = 0;
const int spacing = r.metrics->spacing;
QRect firstDrawRect, secondDrawRect;
auto placeSubIndicator = [&](const QQStyleKitIndicatorProperties *sub,
QRect &drawRect) {
if (!sub)
return;
const QSize sz(qSaturateRound(sub->width()), qSaturateRound(sub->height()));
const QMargins subMargins = elementMargins(sub);
const int slotW = qSaturateRound(sub->leftMargin() + sub->width() + sub->rightMargin());
if (sub->alignment() & Qt::AlignRight) {
const QRect slot(contentRect.right() - rightOffset - slotW, contentRect.top(),
slotW, contentRect.height());
drawRect = d->getAlignedRectInContainer(
slot, sz, Qt::AlignRight | Qt::AlignVCenter, { }, subMargins);
rightOffset += slotW + spacing;
} else {
const QRect slot(contentRect.left() + leftOffset, contentRect.top(), slotW,
contentRect.height());
drawRect = d->getAlignedRectInContainer(
slot, sz, Qt::AlignLeft | Qt::AlignVCenter, { }, subMargins);
leftOffset += slotW + spacing;
}
};
placeSubIndicator(first, firstDrawRect);
placeSubIndicator(second, secondDrawRect);
if (first && menuItem->checkType != QStyleOptionMenuItem::NotCheckable
&& menuItem->checked) {
d->drawControlIndicator(
first, visualRect(menuItem->direction, contentRect, firstDrawRect), p);
}
if (second && menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {
d->drawControlIndicator(
second, visualRect(menuItem->direction, contentRect, secondDrawRect), p);
}
// text
QRect textRect = contentRect
.adjusted(leftOffset, 0, -rightOffset, 0)
.marginsRemoved(r.metrics->textPadding);
if (!menuItem->icon.isNull()) {
const int iconW = menuItem->maxIconWidth;
const QIcon::Mode mode = (menuItem->state & State_Enabled)
? QIcon::Normal
: QIcon::Disabled;
const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, opt, w);
const QPixmap pm = menuItem->icon.pixmap(
QSize(iconExtent, iconExtent), p->device()->devicePixelRatio(), mode);
const QSize pmSize = pm.size() / pm.devicePixelRatio();
const int pmW = pmSize.width();
const int pmH = pmSize.height();
p->drawPixmap(
visualRect(menuItem->direction, textRect,
QRect(textRect.left(),
textRect.top() + (textRect.height() - pmH) / 2, pmW, pmH)),
pm);
textRect.setLeft(textRect.left() + iconW + r.metrics->spacing);
}
const int tabIdx = menuItem->text.indexOf(QLatin1Char('\t'));
const QString label = tabIdx >= 0 ? menuItem->text.left(tabIdx) : menuItem->text;
const QString shortcut = tabIdx >= 0 ? menuItem->text.mid(tabIdx + 1) : QString();
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
QRect labelRect = textRect;
if (!shortcut.isEmpty() && menuItem->reservedShortcutWidth > 0) {
const QRect shortcutRect(textRect.right() - menuItem->reservedShortcutWidth + 1,
textRect.top(), menuItem->reservedShortcutWidth,
textRect.height());
labelRect.setRight(shortcutRect.left() - r.metrics->spacing - 1);
const int shortcutFlags =
int(QStyle::visualAlignment(menuItem->direction,
Qt::AlignRight | Qt::AlignVCenter))
| int(textFlags);
p->save();
p->setFont(r.font().resolve(p->font()));
p->setBrush(Qt::NoBrush);
if (const auto *shortcutText = r.text())
p->setPen(shortcutText->color());
p->drawText(visualRect(menuItem->direction, textRect, shortcutRect), shortcutFlags,
shortcut);
p->restore();
}
d->drawControlText(r.text(), r.font(),
visualRect(menuItem->direction, textRect, labelRect), label,
textFlags, p, Qt::AlignLeft | Qt::AlignVCenter);
return;
}
break;
// no tearoff or bottom scroller support in Controls style,
// so make it consistent and don't draw them
case CE_MenuScroller:
case CE_MenuTearoff:
return;
case CE_MenuEmptyArea:
return;
#endif // QT_CONFIG(menu)
#if QT_CONFIG(menubar)
case CE_MenuBarEmptyArea:
return;
case CE_MenuBarItem:
if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
QStyle::State adjustedState = menuItem->state;
if (adjustedState & State_Selected)
adjustedState |= State_MouseOver;
const auto r = d->resolveSubElement(w, opt, QQStyleKitReader::ControlType::MenuBarItem,
adjustedState);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
const QRect textRect = opt->rect.marginsRemoved(
r.metrics->margins + r.metrics->padding + r.metrics->textPadding);
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
d->drawControlText(r.text(), r.font(), textRect, menuItem->text, textFlags, p);
return;
}
break;
#endif // QT_CONFIG(menubar)
#if QT_CONFIG(tabbar)
case CE_TabBarTab:
if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
const auto r = d->resolveSubElement(w, opt, QQStyleKitReader::ControlType::TabButton,
tab->state);
if (!r.isValid())
break;
const auto &m = *r.metrics;
const QRect backgroundRect = opt->rect.marginsRemoved(m.margins);
d->drawStyledItemRect(r.background(), backgroundRect, p);
const QRect contentRect = backgroundRect.marginsRemoved(m.padding);
uint textFlags = Qt::TextShowMnemonic;
if (!styleHint(SH_UnderlineShortcut, opt, w))
textFlags |= Qt::TextHideMnemonic;
if (!tab->icon.isNull()) {
const QIcon::Mode mode = (tab->state & State_Enabled)
? QIcon::Normal
: QIcon::Disabled;
const QIcon::State iconState = (tab->state & State_Selected)
? QIcon::On
: QIcon::Off;
const QPixmap pm = tab->icon.pixmap(tab->iconSize, p->device()->devicePixelRatio(),
mode, iconState);
const QSize pmSize = pm.size() / pm.devicePixelRatio();
const QRect pr(contentRect.x(),
contentRect.y() + (contentRect.height() - pmSize.height()) / 2,
pmSize.width(), pmSize.height());
p->drawPixmap(visualRect(tab->direction, contentRect, pr), pm);
QRect tr = contentRect;
tr.setLeft(contentRect.x() + pmSize.width() + m.spacing);
d->drawControlText(r.text(), r.font(), visualRect(tab->direction, contentRect, tr),
tab->text, textFlags, p, Qt::AlignCenter);
} else {
d->drawControlText(r.text(), r.font(), contentRect, tab->text, textFlags, p,
Qt::AlignCenter);
}
return;
}
break;
#endif // QT_CONFIG(tabbar)
#if QT_CONFIG(toolbar)
case CE_ToolBar:
if (const auto *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolBar, toolBar->state);
if (!r.isValid())
break;
d->drawStyledItemRect(r.background(), opt->rect.marginsRemoved(r.metrics->margins), p);
return;
}
break;
#endif // QT_CONFIG(toolbar)
default:
break;
}
QCommonStyle::drawControl(element, opt, p, w);
}
/*! \reimp */
QRect QStyleKitStyle::subElementRect(SubElement r, const QStyleOption *opt,
const QWidget *widget) const
{
using QtPrivate::qSaturateRound;
Q_D(const QStyleKitStyle);
switch (r) {
case SE_PushButtonBevel:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
const auto controlType = btn->features & QStyleOptionButton::Flat
? QQStyleKitReader::ControlType::FlatButton
: QQStyleKitReader::ControlType::Button;
const auto resolved = d->resolveLayout(controlType, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect rect = opt->rect.marginsRemoved(metrics.margins);
return visualRect(opt->direction, opt->rect, rect);
}
break;
case SE_PushButtonContents:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
const auto controlType = btn->features & QStyleOptionButton::Flat
? QQStyleKitReader::ControlType::FlatButton
: QQStyleKitReader::ControlType::Button;
const auto resolved = d->resolveLayout(controlType, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect rect = opt->rect.marginsRemoved(
metrics.margins + metrics.padding + metrics.textPadding);
return visualRect(opt->direction, opt->rect, rect);
}
break;
case SE_CheckBoxContents:
case SE_RadioButtonContents: {
const auto controlType = r == SE_CheckBoxContents
? QQStyleKitReader::ControlType::CheckBox
: QQStyleKitReader::ControlType::RadioButton;
const auto resolved = d->resolveLayout(controlType, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect contentsRect = opt->rect.marginsRemoved(metrics.margins + metrics.padding);
const auto subElement = r == SE_CheckBoxContents
? SE_CheckBoxIndicator
: SE_RadioButtonIndicator;
QRect indicatorRect = visualRect(opt->direction, opt->rect,
subElementRect(subElement, opt, widget));
const int spacing = metrics.spacing;
const auto *indicator = resolved.indicator();
const uint alignment = indicator
? resolvedAlignment(indicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
: uint(Qt::AlignLeft | Qt::AlignVCenter);
if (alignment & Qt::AlignLeft) {
contentsRect.setLeft(indicatorRect.right() + spacing + metrics.textPadding.left());
} else if (alignment & Qt::AlignHCenter) {
contentsRect.setLeft(indicatorRect.right() + spacing + metrics.textPadding.left());
contentsRect.setRight(indicatorRect.left() - spacing - metrics.textPadding.right());
} else {
contentsRect.setRight(indicatorRect.left() - spacing - metrics.textPadding.right());
}
return visualRect(opt->direction, opt->rect, contentsRect);
}
case SE_CheckBoxIndicator:
case SE_RadioButtonIndicator: {
const auto controlType = r == SE_CheckBoxIndicator
? QQStyleKitReader::ControlType::CheckBox
: QQStyleKitReader::ControlType::RadioButton;
const auto resolved = d->resolveLayout(controlType, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect rect = opt->rect.marginsRemoved(metrics.margins);
const auto *indicator = resolved.indicator();
if (!indicator || !indicator->visible() || indicator->opacity() == 0)
return rect;
const int w = qSaturateRound(resolvedWidth(
indicator,
rect.width() - metrics.padding.left() - metrics.padding.right()
- metrics.indicatorMargins.left() - metrics.indicatorMargins.right()));
const int h = qSaturateRound(resolvedHeight(
indicator,
rect.height()
- metrics.padding.top() - metrics.padding.bottom()
- metrics.indicatorMargins.top() - metrics.indicatorMargins.bottom()));
const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignLeft,
Qt::AlignVCenter);
const QRect indicatorRect = d->getAlignedRectInContainer(
rect, QSize(w, h), alignment, metrics.padding, metrics.indicatorMargins);
return visualRect(opt->direction, rect, indicatorRect);
}
#if QT_CONFIG(itemviews)
case SE_ItemViewItemCheckIndicator: {
const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect rect = opt->rect.marginsRemoved(metrics.margins);
const auto *indicator = resolved.indicator();
if (!indicator || !indicator->visible() || indicator->opacity() == 0)
return rect;
const int w = qSaturateRound(resolvedWidth(
indicator,
rect.width()
- metrics.padding.left() - metrics.padding.right()
- metrics.indicatorMargins.left() - metrics.indicatorMargins.right()));
const int h = qSaturateRound(resolvedHeight(
indicator,
rect.height() - metrics.padding.top() - metrics.padding.bottom()
- metrics.indicatorMargins.top() - metrics.indicatorMargins.bottom()));
const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignLeft,
Qt::AlignVCenter);
const QRect indicatorRect = d->getAlignedRectInContainer(
rect, QSize(w, h), alignment, metrics.padding, metrics.indicatorMargins);
return visualRect(opt->direction, rect, indicatorRect);
}
case SE_ItemViewItemDecoration:
if (const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
if (!(itemViewOption->features & QStyleOptionViewItem::HasDecoration))
return QRect();
const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect contentsRect = opt->rect.marginsRemoved(metrics.margins + metrics.padding);
if (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator) {
const QRect indicatorRect = subElementRect(
SE_ItemViewItemCheckIndicator, opt, widget);
const auto *checkIndicator = resolved.indicator();
const uint checkAlign = checkIndicator
? resolvedAlignment(checkIndicator->alignment(), Qt::AlignLeft,
Qt::AlignVCenter)
: uint(Qt::AlignLeft | Qt::AlignVCenter);
if (checkAlign & Qt::AlignLeft) {
contentsRect.setLeft(indicatorRect.right()
+ metrics.indicatorMargins.right() + metrics.spacing);
} else if (checkAlign & Qt::AlignRight) {
contentsRect.setRight(indicatorRect.left()
- metrics.indicatorMargins.left() - metrics.spacing);
}
}
const QSize decorationSize = itemViewOption->decorationSize
.boundedTo(contentsRect.size());
QRect decorationRect;
switch (itemViewOption->decorationPosition) {
case QStyleOptionViewItem::Top:
decorationRect = QRect(contentsRect.left(), contentsRect.top(),
contentsRect.width(), decorationSize.height());
break;
case QStyleOptionViewItem::Bottom:
decorationRect = QRect(contentsRect.left(),
contentsRect.bottom() - decorationSize.height() + 1,
contentsRect.width(), decorationSize.height());
break;
case QStyleOptionViewItem::Right:
decorationRect = QRect(contentsRect.right() - decorationSize.width() + 1,
contentsRect.top(), decorationSize.width(),
contentsRect.height());
break;
case QStyleOptionViewItem::Left:
default:
decorationRect = QRect(contentsRect.left(), contentsRect.top(),
decorationSize.width(), contentsRect.height());
break;
}
const uint decorationAlign = resolvedAlignment(itemViewOption->decorationAlignment,
Qt::AlignLeft, Qt::AlignVCenter);
decorationRect = d->getAlignedRectInContainer(
decorationRect, decorationSize, decorationAlign, QMargins(0, 0, 0, 0),
QMargins(0, 0, 0, 0));
return visualRect(opt->direction, opt->rect, decorationRect);
}
break;
case SE_ItemViewItemText:
if (const auto *itemViewOption = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect contentsRect = opt->rect.marginsRemoved(metrics.margins + metrics.padding);
const int spacing = metrics.spacing;
if (itemViewOption->features & QStyleOptionViewItem::HasCheckIndicator) {
const QRect indicatorRect = subElementRect(
SE_ItemViewItemCheckIndicator, opt, widget);
const auto *checkIndicator = resolved.indicator();
const uint checkAlign = checkIndicator
? resolvedAlignment(checkIndicator->alignment(), Qt::AlignLeft,
Qt::AlignVCenter)
: uint(Qt::AlignLeft | Qt::AlignVCenter);
if (checkAlign & Qt::AlignLeft) {
contentsRect.setLeft(
indicatorRect.right() + metrics.indicatorMargins.right() + spacing);
} else if (checkAlign & Qt::AlignRight) {
contentsRect.setRight(
indicatorRect.left() - metrics.indicatorMargins.left() - spacing);
}
}
QRect textRect = contentsRect;
if (itemViewOption->features & QStyleOptionViewItem::HasDecoration) {
const QRect decorationRect = subElementRect(
SE_ItemViewItemDecoration, opt, widget);
switch (itemViewOption->decorationPosition) {
case QStyleOptionViewItem::Top:
textRect.setTop(decorationRect.bottom() + spacing);
break;
case QStyleOptionViewItem::Bottom:
textRect.setBottom(decorationRect.top() - spacing);
break;
case QStyleOptionViewItem::Right:
textRect.setRight(decorationRect.left() - spacing);
break;
case QStyleOptionViewItem::Left:
default:
textRect.setLeft(decorationRect.right() + spacing);
break;
}
}
const auto *textProps = resolved.text();
const uint textAlign = textProps
? resolvedAlignment(textProps->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
: uint(Qt::AlignLeft | Qt::AlignVCenter);
if (textAlign & Qt::AlignLeft) {
textRect.setLeft(textRect.left() + metrics.textPadding.left());
} else if (textAlign & Qt::AlignHCenter) {
textRect.setLeft(textRect.left() + metrics.textPadding.left() / 2);
textRect.setRight(textRect.right() - metrics.textPadding.right() / 2);
} else {
textRect.setRight(textRect.right() - metrics.textPadding.right());
}
if (textAlign & Qt::AlignTop) {
textRect.setTop(textRect.top() + metrics.textPadding.top());
} else if (textAlign & Qt::AlignVCenter) {
textRect.setTop(textRect.top() + metrics.textPadding.top() / 2);
textRect.setBottom(textRect.bottom() - metrics.textPadding.bottom() / 2);
} else {
textRect.setBottom(textRect.bottom() - metrics.textPadding.bottom());
}
return visualRect(opt->direction, opt->rect, textRect);
}
break;
#endif // QT_CONFIG(itemviews)
case SE_PushButtonFocusRect:
case SE_CheckBoxClickRect:
case SE_RadioButtonClickRect:
return opt->rect;
#if QT_CONFIG(progressbar)
case SE_ProgressBarGroove: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ProgressBar,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect rect = opt->rect.marginsRemoved(metrics.margins);
const auto *indicator = resolved.indicator();
if (!indicator || !indicator->visible() || indicator->opacity() == 0)
return rect;
const int w = qSaturateRound(resolvedWidth(
indicator,
rect.width() - metrics.padding.left() - metrics.padding.right()
- metrics.indicatorMargins.left() - metrics.indicatorMargins.right()));
const int h = qSaturateRound(resolvedHeight(
indicator,
rect.height() - metrics.padding.top() - metrics.padding.bottom()
- metrics.indicatorMargins.top() - metrics.indicatorMargins.bottom()));
const uint alignment = resolvedAlignment(indicator->alignment(), Qt::AlignLeft,
Qt::AlignVCenter);
const QRect indicatorRect = d->getAlignedRectInContainer(
rect, QSize(w, h), alignment, metrics.padding, metrics.indicatorMargins);
return visualRect(opt->direction, rect, indicatorRect);
}
case SE_ProgressBarContents: {
if (qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ProgressBar,
opt->state);
if (!resolved.isValid())
break;
const auto *foreground = resolved.indicator()
? resolved.indicator()->foreground()
: nullptr;
if (!foreground || !foreground->visible() || foreground->opacity() == 0)
return QRect();
QRect indicatorRect = subElementRect(SE_ProgressBarGroove, opt, widget);
const int foregroundW = qSaturateRound(resolvedWidth(
foreground,
indicatorRect.width() - foreground->leftMargin() - foreground->rightMargin()));
const int foregroundH = qSaturateRound(resolvedHeight(
foreground,
indicatorRect.height() - foreground->topMargin() - foreground->bottomMargin()));
const uint foregroundAlign = resolvedAlignment(foreground->alignment(), Qt::AlignLeft, Qt::AlignVCenter);
const QMargins foregroundMargins = elementMargins(foreground);
const QRect foregroundRect = d->getAlignedRectInContainer(
indicatorRect, QSize(foregroundW, foregroundH), foregroundAlign,
QMargins(0, 0, 0, 0), foregroundMargins);
return visualRect(opt->direction, opt->rect, foregroundRect);
}
break;
}
#endif // QT_CONFIG(progressbar)
case SE_LineEditContents:
if (qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
#if QT_CONFIG(spinbox)
const bool isInSpinBox = widget && qobject_cast<const QSpinBox *>(widget->parentWidget());
#else
const bool isInSpinBox = false;
#endif
const auto controlType = isInSpinBox ? QQStyleKitReader::ControlType::SpinBox
: QQStyleKitReader::ControlType::TextField;
const auto resolved = d->resolveLayout(controlType, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect contentsRect = opt->rect.marginsRemoved(metrics.padding);
const auto *textProps = resolved.text();
const uint textAlign = resolvedAlignment(textProps ? textProps->alignment() : 0u,
Qt::AlignLeft, Qt::AlignVCenter);
if (textAlign & Qt::AlignLeft)
contentsRect.setLeft(contentsRect.left() + metrics.textPadding.left());
else if (textAlign & Qt::AlignHCenter) {
contentsRect.setLeft(contentsRect.left() + metrics.textPadding.left() / 2);
contentsRect.setRight(contentsRect.right() - metrics.textPadding.right() / 2);
} else {
contentsRect.setRight(contentsRect.right() - metrics.textPadding.right());
}
if (textAlign & Qt::AlignTop)
contentsRect.setTop(contentsRect.top() + metrics.textPadding.top());
else if (textAlign & Qt::AlignVCenter) {
contentsRect.setTop(contentsRect.top() + metrics.textPadding.top() / 2);
contentsRect.setBottom(contentsRect.bottom() - metrics.textPadding.bottom() / 2);
} else {
contentsRect.setBottom(contentsRect.bottom() - metrics.textPadding.bottom());
}
return visualRect(opt->direction, opt->rect, contentsRect);
}
break;
case SE_ShapedFrameContents:
#if QT_CONFIG(combobox)
if (widget && widget->inherits("QComboBoxPrivateContainer")) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Popup,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect contentsRect = opt->rect.marginsRemoved(
metrics.margins + metrics.padding + metrics.textPadding);
return visualRect(opt->direction, opt->rect, contentsRect);
}
#endif
#if QT_CONFIG(textedit)
if (qobject_cast<const QTextEdit *>(widget)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::TextArea,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect contentsRect = opt->rect.marginsRemoved(
metrics.margins + metrics.padding + metrics.textPadding);
return visualRect(opt->direction, opt->rect, contentsRect);
}
#endif // textedit
break;
#if QT_CONFIG(tabbar)
case SE_TabBarTabText:
if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::TabButton,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect rect = tab->rect.marginsRemoved(
metrics.margins + metrics.padding + metrics.textPadding);
if (!tab->icon.isNull())
rect.setLeft(rect.left() + tab->iconSize.width() + metrics.spacing);
return visualRect(tab->direction, tab->rect, rect);
}
break;
case SE_TabBarTearIndicatorRight:
case SE_TabBarTearIndicatorLeft:
case SE_TabBarScrollLeftButton:
case SE_TabBarScrollRightButton:
// Not supported
return QRect();
#endif // QT_CONFIG(tabbar)
#if QT_CONFIG(tabwidget)
case SE_TabWidgetTabContents:
if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
// TabWidget corresponds to Page
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Page, opt->state);
if (!resolved.isValid())
break;
const QRect paneRect = subElementRect(SE_TabWidgetTabPane, opt, widget);
const QRect rect = paneRect.marginsRemoved(resolved.metrics->padding);
return visualRect(opt->direction, opt->rect, rect);
}
break;
#endif // QT_CONFIG(tabwidget)
#if QT_CONFIG(toolbar)
case SE_ToolBarHandle:
// Not supported
return QRect();
#endif // QT_CONFIG(toolbar)
default:
break;
}
return QCommonStyle::subElementRect(r, opt, widget);
}
/*! \reimp */
void QStyleKitStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
QPainter *p, const QWidget *w) const
{
Q_D(const QStyleKitStyle);
switch (cc) {
#if QT_CONFIG(slider)
case CC_Slider:
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::Slider, slider->state);
if (!r.isValid())
break;
const auto &metrics = *r.metrics;
QRect backgroundRect = opt->rect.marginsRemoved(metrics.margins);
// background
d->drawStyledItemRect(r.background(), backgroundRect, p);
// groove
if (slider->subControls & SC_SliderGroove) {
// groove
const auto grooveRect = proxy()->subControlRect(CC_Slider, opt, SC_SliderGroove, w);
const auto *indicator = r.indicator();
d->drawStyledItemRect(indicator, grooveRect, p);
// track
const auto *foreground = indicator ? indicator->foreground() : nullptr;
if (foreground && foreground->visible() && foreground->opacity() > 0) {
const bool isHorizontal = slider->orientation == Qt::Horizontal;
const qreal availableW = grooveRect.width()
- foreground->leftMargin() - foreground->rightMargin();
const qreal availableH = grooveRect.height()
- foreground->topMargin() - foreground->bottomMargin();
const qreal range = slider->maximum - slider->minimum;
const qreal ratio = range > 0
? (slider->sliderPosition - slider->minimum) / range
: 0;
const auto hAlign = foreground->alignment() & Qt::AlignHorizontal_Mask;
const auto vAlign = foreground->alignment() & Qt::AlignVertical_Mask;
const qreal fgW = resolvedWidth(foreground, availableW);
const qreal fgH = resolvedHeight(foreground, availableH);
const qreal fgX = hAlign & Qt::AlignRight
? grooveRect.left() + foreground->leftMargin() + availableW - fgW
: hAlign & Qt::AlignHCenter
? grooveRect.left() + foreground->leftMargin() + (availableW - fgW) / 2
: grooveRect.left() + foreground->leftMargin();
const qreal fgY = vAlign & Qt::AlignBottom
? grooveRect.top() + foreground->topMargin() + availableH - fgH
: vAlign & Qt::AlignVCenter
? grooveRect.top() + foreground->topMargin() + (availableH - fgH) / 2
: grooveRect.top() + foreground->topMargin();
const qreal minW = foreground->minimumWidth();
QRectF trackRect;
if (isHorizontal) {
const qreal trackW = foreground->fillWidth()
? minW + ratio * (fgW - minW)
: ratio * fgW;
trackRect = QRectF(fgX, fgY, trackW, fgH);
} else {
const qreal trackH = foreground->fillHeight()
? minW + ratio * (fgH - minW)
: ratio * fgH;
const qreal trackY = fgY
+ (1.0 - ratio) * (foreground->fillHeight() ? fgH - minW : fgH);
trackRect = QRectF(fgX, trackY, fgW, trackH);
}
// The track is the groove's foreground child, so it inherits the
// indicator's transform.
const QRect foregroundBox = visualRect(
opt->direction, grooveRect, QRectF(fgX, fgY, fgW, fgH).toAlignedRect());
QPainterStateGuard stateGuard(p);
if (applyDelegateTransform(p, indicator, grooveRect)
&& applyDelegateTransform(p, foreground, foregroundBox)) {
d->drawStyledItemContents(
foreground,
visualRect(opt->direction, grooveRect, trackRect.toAlignedRect()), p);
}
}
}
// handle
if (slider->subControls & SC_SliderHandle) {
QStyleOptionSlider handleOpt(*slider);
handleOpt.rect = subControlRect(CC_Slider, opt, SC_SliderHandle, w);
const auto *handle = r.handle();
if (handle && handle->visible() && handle->opacity() > 0)
d->drawStyledItemRect(handle, handleOpt.rect, p);
}
return;
}
break;
#endif // QT_CONFIG(slider)
#if QT_CONFIG(dial)
case CC_Dial:
if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::Dial, dial->state);
if (!r.isValid())
break;
const QRectF backgroundRect = QRectF(opt->rect).marginsRemoved(r.metrics->margins);
d->drawStyledItemRect(r.background(), backgroundRect, p);
const auto *handle = r.handle();
if (handle && handle->visible() && handle->opacity() > 0) {
QPainterStateGuard stateGuard(p);
p->translate(backgroundRect.center());
p->rotate(dialAngle(dial));
p->translate(dialHandleOffset(backgroundRect, handle));
d->drawStyledItemRect(handle, QRectF(-handle->width() / 2, -handle->height() / 2,
handle->width(), handle->height()), p);
}
return;
}
break;
#endif // QT_CONFIG(dial)
#if QT_CONFIG(combobox)
case CC_ComboBox:
if (const QStyleOptionComboBox *combo =
qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
// background
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ComboBox, combo->state);
if (!r.isValid())
break;
QRect frameRect = subControlRect(CC_ComboBox, opt, SC_ComboBoxFrame, w);
d->drawStyledItemRect(r.background(), frameRect, p);
// indicator
if (combo->subControls & SC_ComboBoxArrow) {
QStyleOptionComboBox indicatorOpt(*combo);
indicatorOpt.rect = subControlRect(CC_ComboBox, opt, SC_ComboBoxArrow, w);
proxy()->drawPrimitive(PE_IndicatorArrowDown, &indicatorOpt, p, w);
}
// The editable combobox paints its own line edit using the QPalette::Text role
// for the text color, so update that color in the palette
if (auto *cb = qobject_cast<const QComboBox *>(w); cb && cb->isEditable()) {
if (const auto *txt = r.text(); txt && txt->isDefined(QQSK::Property::Color)) {
QPalette stylePalette;
stylePalette.setColor(QPalette::Text, txt->color());
d->setStylePalette(cb->lineEdit(), stylePalette);
}
}
return;
}
break;
#endif // QT_CONFIG(combobox)
#if QT_CONFIG(spinbox)
case CC_SpinBox:
if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::SpinBox, spin->state);
if (!r.isValid())
break;
QRect frameRect = opt->rect.marginsRemoved(r.metrics->margins);
// background
d->drawStyledItemRect(r.background(), frameRect, p);
// up/down buttons
if (spin->subControls & SC_SpinBoxUp) {
QStyleOptionSpinBox upOpt(*spin);
upOpt.rect = subControlRect(CC_SpinBox, opt, SC_SpinBoxUp, w);
proxy()->drawPrimitive(PE_IndicatorSpinUp, &upOpt, p, w);
}
if (spin->subControls & SC_SpinBoxDown) {
QStyleOptionSpinBox downOpt(*spin);
downOpt.rect = subControlRect(CC_SpinBox, opt, SC_SpinBoxDown, w);
proxy()->drawPrimitive(PE_IndicatorSpinDown, &downOpt, p, w);
}
// The spinbox line edit paints its text using the QPalette::Text role,
// so update that color in the palette
if (auto *sb = qobject_cast<const QSpinBox *>(w)) {
if (const auto *txt = r.text(); txt && txt->isDefined(QQSK::Property::Color)) {
QLineEdit *lineEdit = sb->findChild<QLineEdit *>();
if (lineEdit) {
QPalette p = lineEdit->palette();
if (p.color(QPalette::Text) != txt->color()) {
p.setColor(QPalette::Text, txt->color());
lineEdit->setPalette(p);
}
}
}
}
return;
}
break;
#endif // QT_CONFIG(spinbox)
#if QT_CONFIG(groupbox)
case CC_GroupBox:
if (const QStyleOptionGroupBox *groupBox =
qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::GroupBox, groupBox->state);
if (!r.isValid())
break;
// background/frame
if (groupBox->subControls & SC_GroupBoxFrame) {
QStyleOptionFrame frameOpt;
frameOpt.QStyleOption::operator=(*groupBox);
frameOpt.rect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, w);
drawPrimitive(PE_FrameGroupBox, &frameOpt, p, w);
}
// title
if (groupBox->subControls & SC_GroupBoxLabel && !groupBox->text.isEmpty()) {
const auto *textProps = r.text();
const QFont textFont = r.font();
QRect titleRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, w);
d->drawControlText(textProps, textFont,
titleRect.marginsRemoved(r.metrics->textPadding), groupBox->text,
Qt::TextShowMnemonic, p, Qt::AlignLeft | Qt::AlignVCenter);
}
// don't draw checkmark as the Controls style doesn't draw it
// and StyleKit doesn't provide a way to style it
return;
}
break;
#endif // QT_CONFIG(groupbox)
#if QT_CONFIG(scrollbar)
case CC_ScrollBar:
if (const auto *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ScrollBar,
scrollbar->state);
if (!r.isValid())
break;
// background = groove
if (scrollbar->subControls & SC_ScrollBarGroove) {
QRect backgroundRect = proxy()->subControlRect(
CC_ScrollBar, opt, SC_ScrollBarGroove, w);
d->drawStyledItemRect(r.background(), backgroundRect, p);
}
// TODO: increase button
// TODO: decrease button
// slider = indicator
if (scrollbar->subControls & SC_ScrollBarSlider) {
QStyleOptionSlider newScrollbar(*scrollbar);
newScrollbar.rect = proxy()->subControlRect(
CC_ScrollBar, opt, SC_ScrollBarSlider, w);
proxy()->drawControl(CE_ScrollBarSlider, &newScrollbar, p, w);
}
return;
}
break;
#endif // QT_CONFIG(scrollbar)
#if QT_CONFIG(toolbutton)
case CC_ToolButton:
if (const QStyleOptionToolButton *tool =
qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
const auto r = d->resolve(w, QQStyleKitReader::ControlType::ToolButton, tool->state);
if (!r.isValid())
break;
// background
d->drawStyledItemRect(r.background(), opt->rect, p);
// menu arrow indicator
if ((tool->features
& (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::PopupDelay))
== QStyleOptionToolButton::MenuButtonPopup
&& (tool->subControls & SC_ToolButtonMenu)) {
const QRect menuRect = subControlRect(CC_ToolButton, opt, SC_ToolButtonMenu, w);
d->drawControlIndicator(r.indicator(), menuRect, p);
}
// label
State bflags = tool->state & ~State_Sunken;
if (bflags & State_AutoRaise) {
if (!(bflags & State_MouseOver) || !(bflags & State_Enabled))
bflags &= ~State_Raised;
}
if (tool->state & State_Sunken && tool->activeSubControls & SC_ToolButton)
bflags |= State_Sunken;
QStyleOptionToolButton label = *tool;
label.state = bflags;
label.rect = subControlRect(CC_ToolButton, opt, SC_ToolButton, w);
proxy()->drawControl(CE_ToolButtonLabel, &label, p, w);
return;
}
break;
#endif // QT_CONFIG(toolbutton)
default:
break;
}
QCommonStyle::drawComplexControl(cc, opt, p, w);
}
/*! \reimp */
QStyle::SubControl QStyleKitStyle::hitTestComplexControl(ComplexControl cc,
const QStyleOptionComplex *opt,
const QPoint &pt, const QWidget *w) const
{
return QCommonStyle::hitTestComplexControl(cc, opt, pt, w);
}
/*! \reimp */
QRect QStyleKitStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
SubControl sc, const QWidget *w) const
{
using QtPrivate::qSaturateRound;
Q_D(const QStyleKitStyle);
switch (cc) {
#if QT_CONFIG(slider)
case CC_Slider:
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Slider,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const bool horizontal = slider->orientation == Qt::Horizontal;
QRect contentsRect = opt->rect
.marginsRemoved(metrics.margins)
.marginsRemoved(metrics.padding);
const auto *indicator = resolved.indicator();
if (!indicator)
return contentsRect;
switch (sc) {
case SC_SliderGroove: {
const auto availableW = contentsRect.width()
- indicator->leftMargin() - indicator->rightMargin();
const auto availableH = contentsRect.height()
- indicator->topMargin() - indicator->bottomMargin();
const qreal grooveW = resolvedWidth(indicator, availableW);
const qreal grooveH = resolvedHeight(indicator, availableH);
QRectF grooveRect(0, 0, grooveW, grooveH);
const uint rawHAlign = indicator->alignment() & Qt::AlignHorizontal_Mask;
const uint rawVAlign = indicator->alignment() & Qt::AlignVertical_Mask;
const uint hAlign = rawHAlign
? static_cast<Qt::Alignment>(rawHAlign)
: Qt::AlignLeft;
const uint vAlign = rawVAlign
? static_cast<Qt::Alignment>(rawVAlign)
: Qt::AlignVCenter;
if (hAlign & Qt::AlignLeft) {
grooveRect.moveLeft(contentsRect.x() + indicator->leftMargin());
} else if (hAlign & Qt::AlignHCenter) {
const qreal availableWidth = contentsRect.width()
- indicator->leftMargin() - indicator->rightMargin();
grooveRect.moveLeft(contentsRect.x() + indicator->leftMargin()
+ (availableWidth - grooveW) / 2.0);
} else {
grooveRect.moveLeft(contentsRect.x() + contentsRect.width()
- indicator->rightMargin() - grooveW);
}
if (vAlign & Qt::AlignTop) {
grooveRect.moveTop(contentsRect.y() + indicator->topMargin());
} else if (vAlign & Qt::AlignVCenter) {
const qreal availableHeight = contentsRect.height()
- indicator->topMargin() - indicator->bottomMargin();
grooveRect.moveTop(contentsRect.y()
+ indicator->topMargin()
+ (availableHeight - grooveH) / 2.0);
} else {
grooveRect.moveTop(contentsRect.y()
+ contentsRect.height()
- indicator->bottomMargin() - grooveH);
}
return visualRect(opt->direction, opt->rect, grooveRect.toAlignedRect());
}
case SC_SliderHandle: {
const auto *handle = resolved.handle();
if (!handle || !handle->visible() || handle->opacity() == 0)
return contentsRect;
const qreal handleW = resolvedWidth(
handle, contentsRect.width() - handle->leftMargin() - handle->rightMargin());
const qreal handleH = resolvedHeight(
handle, contentsRect.height() - handle->topMargin() - handle->bottomMargin());
QRectF handleRect(0, 0, handleW, handleH);
if (horizontal) {
const int range = qSaturateRound(contentsRect.width()
- handle->leftMargin() - handle->rightMargin() - handleW);
const int sliderPos = QStyle::sliderPositionFromValue(
slider->minimum, slider->maximum, slider->sliderPosition, range, false);
handleRect.moveLeft(contentsRect.x() + handle->leftMargin() + sliderPos);
handleRect.moveTop(contentsRect.y()
+ handle->topMargin() - handle->bottomMargin()
+ (contentsRect.height() - handleH) / 2.0);
} else {
const int range = qSaturateRound(contentsRect.height()
- handle->topMargin() - handle->bottomMargin() - handleH);
const int sliderPos = QStyle::sliderPositionFromValue(
slider->minimum, slider->maximum, slider->sliderPosition, range, true);
handleRect.moveLeft(contentsRect.x()
+ handle->leftMargin() - handle->rightMargin()
+ (contentsRect.width() - handleW) / 2.0);
handleRect.moveTop(contentsRect.y() + handle->topMargin() + sliderPos);
}
return visualRect(opt->direction, opt->rect, handleRect.toAlignedRect());
}
default:
break;
}
}
break;
#endif // QT_CONFIG(slider)
#if QT_CONFIG(dial)
case CC_Dial:
if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Dial, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const QRectF backgroundRect(opt->rect.marginsRemoved(metrics.margins));
if (sc == SC_DialGroove)
return visualRect(opt->direction, opt->rect, backgroundRect.toAlignedRect());
if (sc != SC_DialHandle) {
return sc == SC_DialTickmarks
? QRect() : QRectF(opt->rect.marginsRemoved(metrics.padding)).toAlignedRect();
}
const auto *handle = resolved.handle();
if (!handle)
return QRect();
const QPointF offset = dialHandleOffset(backgroundRect, handle);
const QPointF center = backgroundRect.center()
+ QTransform().rotate(dialAngle(dial)).map(offset);
const QRectF handleRect(center.x() - handle->width() / 2,
center.y() - handle->height() / 2,
handle->width(), handle->height());
return visualRect(opt->direction, opt->rect, handleRect.toAlignedRect());
}
break;
#endif // QT_CONFIG(dial)
#if QT_CONFIG(combobox)
case CC_ComboBox:
if (const QStyleOptionComboBox *combo =
qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
const auto r = d->resolveLayout(QQStyleKitReader::ControlType::ComboBox, combo->state);
if (!r.isValid())
break;
const auto &metrics = *r.metrics;
QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
switch (sc) {
case SC_ComboBoxFrame:
return visualRect(opt->direction, opt->rect, frameRect);
case SC_ComboBoxEditField: {
QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
QRect indicatorRect = subControlRect(CC_ComboBox, opt, SC_ComboBoxArrow, w);
const int spacing = metrics.spacing;
const auto *indicator = r.indicator();
const uint indicatorAlign = indicator
? resolvedAlignment(indicator->alignment(), Qt::AlignRight, Qt::AlignVCenter)
: uint(Qt::AlignRight | Qt::AlignVCenter);
if (indicatorAlign & Qt::AlignLeft) {
contentsRect.setLeft(indicatorRect.right() + spacing);
} else if (indicatorAlign & Qt::AlignRight) {
contentsRect.setRight(indicatorRect.left() - spacing);
}
return visualRect(opt->direction, opt->rect, contentsRect);
}
case SC_ComboBoxArrow: {
QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
const auto *indicator = r.indicator();
if (!indicator || !indicator->visible() || indicator->opacity() == 0)
return QRect();
const int w = qSaturateRound(resolvedWidth(
indicator,
contentsRect.width() - indicator->leftMargin() - indicator->rightMargin()));
const int h = qSaturateRound(resolvedHeight(
indicator,
contentsRect.height() - indicator->topMargin() - indicator->bottomMargin()));
const uint indicatorAlign = resolvedAlignment(
indicator->alignment(), Qt::AlignRight, Qt::AlignVCenter);
const QMargins indicatorMargins = elementMargins(indicator);
return visualRect(
opt->direction, opt->rect,
d->getAlignedRectInContainer(contentsRect, QSize(w, h),
indicatorAlign, QMargins(),
indicatorMargins));
}
case SC_ComboBoxListBoxPopup: {
QRect popupRect = opt->rect;
popupRect.setTop(opt->rect.bottom());
return visualRect(opt->direction, opt->rect, popupRect);
}
default:
break;
}
}
break;
#endif // QT_CONFIG(combobox)
#if QT_CONFIG(spinbox)
case CC_SpinBox:
if (const QStyleOptionSpinBox *spinBox =
qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
const auto r = d->resolveLayout(QQStyleKitReader::ControlType::SpinBox, spinBox->state);
if (!r.isValid())
break;
const auto &metrics = *r.metrics;
QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
switch (sc) {
case SC_SpinBoxFrame:
return visualRect(opt->direction, opt->rect, frameRect);
case SC_SpinBoxEditField: {
QRect upIndicatorRect = subControlRect(CC_SpinBox, opt, SC_SpinBoxUp, w);
QRect downIndicatorRect = subControlRect(CC_SpinBox, opt, SC_SpinBoxDown, w);
const int spacing = metrics.spacing;
const auto *upIndicator = r.indicator() ? r.indicator()->first() : nullptr;
const auto *downIndicator = r.indicator() ? r.indicator()->second() : nullptr;
const bool hasUpIndicator = upIndicator
&& upIndicator->visible() && upIndicator->opacity() > 0;
const bool hasDownIndicator = downIndicator
&& downIndicator->visible() && downIndicator->opacity() > 0;
const uint upAlign = hasUpIndicator
? resolvedAlignment(upIndicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
: uint(Qt::AlignLeft | Qt::AlignVCenter);
const uint downAlign = hasDownIndicator
? resolvedAlignment(downIndicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter)
: uint(Qt::AlignLeft | Qt::AlignVCenter);
if (hasUpIndicator && hasDownIndicator) {
if ((upAlign & Qt::AlignLeft) && (downAlign & Qt::AlignRight)) {
contentsRect.setLeft(upIndicatorRect.right() + spacing);
contentsRect.setRight(downIndicatorRect.left() - spacing);
} else if ((upAlign & Qt::AlignRight) && (downAlign & Qt::AlignLeft)) {
contentsRect.setRight(upIndicatorRect.left() - spacing);
contentsRect.setLeft(downIndicatorRect.right() + spacing);
}
} else if (hasUpIndicator) {
if (upAlign & Qt::AlignLeft) {
contentsRect.setLeft(upIndicatorRect.right() + spacing);
} else if (upAlign & Qt::AlignRight) {
contentsRect.setRight(upIndicatorRect.left() - spacing);
}
} else if (hasDownIndicator) {
if (downAlign & Qt::AlignLeft) {
contentsRect.setLeft(downIndicatorRect.right() + spacing);
} else if (downAlign & Qt::AlignRight) {
contentsRect.setRight(downIndicatorRect.left() - spacing);
}
}
return visualRect(opt->direction, opt->rect, contentsRect);
}
case SC_SpinBoxUp: {
const auto *upIndicator = r.indicator() ? r.indicator()->first() : nullptr;
if (!upIndicator || !upIndicator->visible() || upIndicator->opacity() == 0)
return contentsRect;
const int w = qSaturateRound(resolvedWidth(
upIndicator,
contentsRect.width() - upIndicator->leftMargin() - upIndicator->rightMargin()));
const int h = qSaturateRound(resolvedHeight(
upIndicator,
contentsRect.height() - upIndicator->topMargin() - upIndicator->bottomMargin()));
const uint upAlign = resolvedAlignment(
upIndicator->alignment(), Qt::AlignLeft, Qt::AlignVCenter);
const QMargins upMargins = elementMargins(upIndicator);
return visualRect(opt->direction, opt->rect,
d->getAlignedRectInContainer(contentsRect, QSize(w, h), upAlign,
QMargins(), upMargins));
}
case SC_SpinBoxDown: {
const auto *downIndicator = r.indicator() ? r.indicator()->second() : nullptr;
if (!downIndicator || !downIndicator->visible() || downIndicator->opacity() == 0)
return frameRect;
const int w = qSaturateRound(resolvedWidth(
downIndicator,
contentsRect.width()
- downIndicator->leftMargin() - downIndicator->rightMargin()));
const int h = qSaturateRound(resolvedHeight(
downIndicator,
contentsRect.height()
- downIndicator->topMargin() - downIndicator->bottomMargin()));
const uint downAlign = resolvedAlignment(downIndicator->alignment(), Qt::AlignLeft,
Qt::AlignVCenter);
const QMargins downMargins = elementMargins(downIndicator);
return visualRect(opt->direction, opt->rect,
d->getAlignedRectInContainer(contentsRect, QSize(w, h), downAlign,
QMargins(), downMargins));
}
default:
break;
}
}
break;
#endif // QT_CONFIG(spinbox)
#if QT_CONFIG(groupbox)
case CC_GroupBox:
if (const QStyleOptionGroupBox *groupBox =
qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
const auto r = d->resolveLayout(QQStyleKitReader::ControlType::GroupBox,
groupBox->state);
if (!r.isValid())
break;
const auto &metrics = *r.metrics;
QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
switch (sc) {
case SC_GroupBoxFrame:
return visualRect(opt->direction, opt->rect, frameRect);
case SC_GroupBoxLabel: {
const int fontHeight = opt->fontMetrics.height();
const int labelHeight = metrics.textPadding.top()
+ fontHeight + metrics.textPadding.bottom();
const QRect labelContainer(opt->rect.left(), opt->rect.top(), opt->rect.width(),
labelHeight);
return visualRect(opt->direction, opt->rect, labelContainer);
}
case SC_GroupBoxCheckBox: {
// The controls style doesn't draw the checkbox, so we don't return a rect for it
return QRect();
}
case SC_GroupBoxContents: {
const auto labelHeight = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, w)
.height();
const auto offset = metrics.padding.top()
+ qMax(labelHeight, metrics.margins.top() + metrics.spacing);
QRect contentsRect(
opt->rect.left() + metrics.padding.left(), opt->rect.top() + offset,
opt->rect.width() - metrics.padding.left() - metrics.padding.right(),
opt->rect.bottom() - opt->rect.top() - offset - metrics.padding.bottom());
return visualRect(opt->direction, opt->rect, contentsRect);
}
default:
break;
}
}
break;
#endif // QT_CONFIG(groupbox)
#if QT_CONFIG(scrollbar)
case CC_ScrollBar:
if (const auto *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ScrollBar,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const bool horizontal = scrollbar->orientation == Qt::Horizontal;
const QRectF grooveRect = opt->rect.marginsRemoved(metrics.margins);
switch (sc) {
// TODO: support up/down buttons
case SC_ScrollBarAddLine:
case SC_ScrollBarSubLine:
case SC_ScrollBarSubPage:
case SC_ScrollBarAddPage:
return QRect();
case SC_ScrollBarGroove:
return visualRect(opt->direction, opt->rect, grooveRect.toAlignedRect());
case SC_ScrollBarSlider: {
const auto *indicator = resolved.indicator();
if (!indicator || !indicator->visible() || indicator->opacity() == 0)
return QRect();
const QRectF contentsRect = grooveRect.marginsRemoved(metrics.padding);
const qreal availableW = contentsRect.width()
- indicator->leftMargin() - indicator->rightMargin();
const qreal availableH = contentsRect.height()
- indicator->topMargin() - indicator->bottomMargin();
const int totalRange = scrollbar->maximum
- scrollbar->minimum + scrollbar->pageStep;
const qreal ratio = totalRange > 0 ? qreal(scrollbar->pageStep) / totalRange : 1.0;
QRectF sliderRect;
if (horizontal) {
const qreal sliderW = qMax(
qreal(metrics.indicatorImplicitSize.width()), availableW * ratio);
const qreal sliderH = resolvedHeight(indicator, availableH);
const qreal travelRange = qMax(0.0, availableW - sliderW);
const int sliderPos = QStyle::sliderPositionFromValue(
scrollbar->minimum, scrollbar->maximum, scrollbar->sliderPosition,
int(travelRange), scrollbar->upsideDown);
sliderRect = QRectF(
contentsRect.x() + indicator->leftMargin() + sliderPos,
contentsRect.y() + indicator->topMargin() + (availableH - sliderH) / 2.0,
sliderW, sliderH);
} else {
const qreal sliderH = qMax(
qreal(metrics.indicatorImplicitSize.width()), availableH * ratio);
const qreal sliderW = resolvedWidth(indicator, availableW);
const qreal travelRange = qMax(0.0, availableH - sliderH);
const int sliderPos = QStyle::sliderPositionFromValue(
scrollbar->minimum, scrollbar->maximum, scrollbar->sliderPosition,
int(travelRange), scrollbar->upsideDown);
sliderRect = QRectF(
contentsRect.x() + indicator->leftMargin() + (availableW - sliderW) / 2.0,
contentsRect.y() + indicator->topMargin() + sliderPos, sliderW, sliderH);
}
return visualRect(opt->direction, opt->rect, sliderRect.toAlignedRect());
}
default:
break;
}
}
break;
#endif // QT_CONFIG(scrollbar)
#if QT_CONFIG(toolbutton)
case CC_ToolButton:
if (const auto *tool = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolButton,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QRect frameRect = opt->rect.marginsRemoved(metrics.margins);
switch (sc) {
case SC_ToolButton: {
QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
const bool hasMenuButton = (tool->features
& (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::HasMenu))
!= 0;
const auto *indicator = resolved.indicator();
if (hasMenuButton && indicator && indicator->visible()
&& indicator->opacity() > 0) {
const QRect menuRect = subControlRect(CC_ToolButton, opt, SC_ToolButtonMenu, w);
const int spacing = metrics.spacing;
const uint indicatorAlign = resolvedAlignment(indicator->alignment(),
Qt::AlignRight, Qt::AlignVCenter);
if (indicatorAlign & Qt::AlignLeft)
contentsRect.setLeft(menuRect.right() + spacing);
else if (indicatorAlign & Qt::AlignRight)
contentsRect.setRight(menuRect.left() - spacing);
}
return visualRect(opt->direction, opt->rect, contentsRect);
}
case SC_ToolButtonMenu: {
QRect contentsRect = frameRect.marginsRemoved(metrics.padding);
if ((tool->features
& (QStyleOptionToolButton::MenuButtonPopup
| QStyleOptionToolButton::PopupDelay))
!= QStyleOptionToolButton::MenuButtonPopup) {
break;
}
const auto *indicator = resolved.indicator();
if (!indicator || !indicator->visible() || indicator->opacity() == 0)
return QRect();
const int w = qSaturateRound(resolvedWidth(
indicator,
contentsRect.width() - indicator->leftMargin() - indicator->rightMargin()));
const int h = qSaturateRound(resolvedHeight(
indicator,
contentsRect.height() - indicator->topMargin() - indicator->bottomMargin()));
const uint indicatorAlign = resolvedAlignment(indicator->alignment(),
Qt::AlignRight, Qt::AlignVCenter);
const QMargins indicatorMargins = elementMargins(indicator);
return visualRect(opt->direction, opt->rect,
d->getAlignedRectInContainer(contentsRect, QSize(w, h), indicatorAlign, QMargins(), indicatorMargins));
}
default:
break;
}
}
break;
#endif // QT_CONFIG(toolbutton)
default:
break;
}
return QCommonStyle::subControlRect(cc, opt, sc, w);
}
/*! \reimp */
QSize QStyleKitStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
const QSize &contentsSize, const QWidget *widget) const
{
Q_D(const QStyleKitStyle);
switch (ct) {
#if QT_CONFIG(pushbutton)
case CT_PushButton:
#endif
#if QT_CONFIG(toolbutton)
case CT_ToolButton: {
auto controlType = QQStyleKitReader::ControlType::ToolButton;
if (const auto *buttonOpt = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
controlType = (buttonOpt->features & QStyleOptionButton::Flat)
? QQStyleKitReader::ControlType::FlatButton
: QQStyleKitReader::ControlType::Button;
}
const auto resolved = d->resolveLayout(controlType, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const QSize contentSize = contentsSize
+ QSize(metrics.padding.left() + metrics.padding.right(),
metrics.padding.top() + metrics.padding.bottom())
+ QSize(metrics.textPadding.left() + metrics.textPadding.right(),
metrics.textPadding.top() + metrics.textPadding.bottom());
const QSize bgSize = metrics.bgImplicitSize
+ QSize(metrics.margins.left() + metrics.margins.right(),
metrics.margins.top() + metrics.margins.bottom());
return contentSize.expandedTo(bgSize);
}
#endif
case CT_CheckBox:
case CT_RadioButton:
if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
const auto controlType = ct == CT_CheckBox ? QQStyleKitReader::ControlType::CheckBox
: QQStyleKitReader::ControlType::RadioButton;
const auto resolved = d->resolveLayout(controlType, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const QSize textSize = opt->fontMetrics.size(Qt::TextShowMnemonic, btn->text);
const int bgWidth = metrics.bgImplicitSize.width()
+ metrics.margins.left() + metrics.margins.right();
const int bgHeight = metrics.bgImplicitSize.height()
+ metrics.margins.top() + metrics.margins.bottom();
const int contentWidth = std::max(textSize.width(), contentsSize.width())
+ metrics.padding.left() + metrics.padding.right() + metrics.textPadding.left()
+ metrics.textPadding.right();
const int contentHeight = std::max(textSize.height(), contentsSize.height())
+ metrics.padding.top() + metrics.padding.bottom() + metrics.textPadding.top()
+ metrics.textPadding.bottom();
const int indicatorWidth = metrics.indicatorImplicitSize.width()
+ metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
const int indicatorHeight = metrics.indicatorImplicitSize.height()
+ metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
return QSize(std::max({ contentWidth + indicatorWidth + metrics.spacing, bgWidth }),
std::max({ contentHeight, indicatorHeight, bgHeight }));
}
break;
#if QT_CONFIG(itemviews)
case CT_ItemViewItem:
if (const auto *item = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
const auto resolved = d->resolveLayout(itemViewControlType(opt), opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const QSize textSize = opt->fontMetrics.size(Qt::TextShowMnemonic, item->text);
const int bgWidth = metrics.bgImplicitSize.width()
+ metrics.margins.left() + metrics.margins.right();
const int bgHeight = metrics.bgImplicitSize.height()
+ metrics.margins.top() + metrics.margins.bottom();
const int contentWidth = std::max(textSize.width(), contentsSize.width())
+ metrics.padding.left() + metrics.padding.right() + metrics.textPadding.left()
+ metrics.textPadding.right();
const int contentHeight = std::max(textSize.height(), contentsSize.height())
+ metrics.padding.top() + metrics.padding.bottom() + metrics.textPadding.top()
+ metrics.textPadding.bottom();
const int indicatorWidth = metrics.indicatorImplicitSize.width()
+ metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
const int indicatorHeight = metrics.indicatorImplicitSize.height()
+ metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
return QSize(std::max({ contentWidth + indicatorWidth + metrics.spacing, bgWidth }),
std::max({ contentHeight, indicatorHeight, bgHeight }));
}
break;
#endif // QT_CONFIG(itemviews)
case CT_ProgressBar:
if (qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ProgressBar,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const auto indicatorW = metrics.indicatorImplicitSize.width()
+ metrics.indicatorMargins.left()
+ metrics.indicatorMargins.right();
const auto indicatorH = metrics.indicatorImplicitSize.height()
+ metrics.indicatorMargins.top()
+ metrics.indicatorMargins.bottom();
const int bgW = metrics.bgImplicitSize.width()
+ metrics.margins.left() + metrics.margins.right();
const int bgH = metrics.bgImplicitSize.height()
+ metrics.margins.top() + metrics.margins.bottom();
// For progress bar in Controls, the content size is based on the indicator size
const int contentW = indicatorW + metrics.padding.left() + metrics.padding.right();
const int contentH = indicatorH + metrics.padding.top() + metrics.padding.bottom();
return QSize(std::max(contentW, bgW), std::max(contentH, bgH));
}
break;
case CT_Slider:
if (qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Slider,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
// background = indicator = groove + track
const int bgW = std::max(
metrics.padding.left() + metrics.padding.right()
+ metrics.indicatorImplicitSize.width()
+ metrics.indicatorMargins.left() + metrics.indicatorMargins.right(),
metrics.bgImplicitSize.width() + metrics.margins.left() + metrics.margins.right());
const int bgH = std::max(
metrics.padding.top() + metrics.padding.bottom()
+ metrics.indicatorImplicitSize.height()
+ metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom(),
metrics.bgImplicitSize.height() + metrics.margins.top() + metrics.margins.bottom());
const int handleW = metrics.handleImplicitSize.width()
+ metrics.padding.left()
+ metrics.padding.right();
const int handleH = metrics.handleImplicitSize.height()
+ metrics.padding.top()
+ metrics.padding.bottom();
return QSize(std::max(handleW, bgW), std::max(handleH, bgH));
}
break;
#if QT_CONFIG(lineedit)
case CT_LineEdit:
if (const auto *lineEdit = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
QStyleOption lineEditOpt(*lineEdit);
lineEditOpt.state &= ~QStyle::State_Sunken;
# if QT_CONFIG(spinbox)
const bool isInSpinBox = widget && qobject_cast<const QSpinBox *>(widget->parent());
# else
const bool isInSpinBox = false;
# endif
auto controlType = isInSpinBox ? QQStyleKitReader::ControlType::SpinBox
: QQStyleKitReader::ControlType::TextField;
const auto resolved = d->resolveLayout(controlType, lineEditOpt.state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
QSize bgSize(0, 0);
// For spinbox, the line edit doesn't have its own background in the Controls style
if (!isInSpinBox)
bgSize = metrics.bgImplicitSize.grownBy(metrics.margins);
const QSize contentSizeWithPadding = contentsSize
.grownBy(metrics.textPadding)
.grownBy(metrics.padding);
return contentSizeWithPadding.expandedTo(bgSize);
}
break;
#endif // QT_CONFIG(lineedit)
#if QT_CONFIG(combobox)
case CT_ComboBox:
if (const auto *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ComboBox,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const QSize textSize = opt->fontMetrics.size(
Qt::TextShowMnemonic, comboBox->currentText);
const int bgW = metrics.bgImplicitSize.width()
+ metrics.margins.left() + metrics.margins.right();
const int bgH = metrics.bgImplicitSize.height()
+ metrics.margins.top() + metrics.margins.bottom();
const int contentW = textSize.width()
+ metrics.padding.left() + metrics.padding.right()
+ metrics.textPadding.left() + metrics.textPadding.right();
const int contentH = textSize.height()
+ metrics.padding.top() + metrics.padding.bottom()
+ metrics.textPadding.top() + metrics.textPadding.bottom();
const int indicatorW = metrics.indicatorImplicitSize.width()
+ metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
const int indicatorH = metrics.indicatorImplicitSize.height()
+ metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
return QSize(std::max({ contentW + indicatorW + metrics.spacing, bgW }),
std::max({ contentH, indicatorH, bgH }));
}
break;
#endif // QT_CONFIG(combobox)
#if QT_CONFIG(spinbox)
case CT_SpinBox: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::SpinBox, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const int bgW = metrics.bgImplicitSize.width()
+ metrics.margins.left() + metrics.margins.right();
const int bgH = metrics.bgImplicitSize.height()
+ metrics.margins.top() + metrics.margins.bottom();
const int contentW = contentsSize.width()
+ metrics.padding.left() + metrics.padding.right()
+ metrics.textPadding.left() + metrics.textPadding.right();
const int contentH = contentsSize.height()
+ metrics.padding.top() + metrics.padding.bottom()
+ metrics.textPadding.top() + metrics.textPadding.bottom();
// TODO: Support vertical layout for spinbox, currently we assume horizontal layout
// TODO: Calculate each indicator (up/down) size separately if they have different sizes in
// the style
const int indicatorW = (metrics.indicatorImplicitSize.width()
+ metrics.indicatorMargins.left() + metrics.indicatorMargins.right()) * 2;
const int indicatorH = metrics.indicatorImplicitSize.height()
+ metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
return QSize(std::max({ contentW + indicatorW + metrics.spacing, bgW }),
std::max({ contentH, indicatorH, bgH }));
}
#endif // QT_CONFIG(spinbox)
#if QT_CONFIG(tabbar)
case CT_TabBarTab:
if (qstyleoption_cast<const QStyleOptionTab *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::TabButton,
opt->state);
if (!resolved.isValid())
break;
const auto &m = *resolved.metrics;
const auto bgSize = m.bgImplicitSize.grownBy(m.margins);
const auto contentSizeWithPadding = contentsSize.grownBy(m.padding + m.textPadding);
return contentSizeWithPadding.expandedTo(bgSize);
}
break;
#endif // QT_CONFIG(tabbar)
#if QT_CONFIG(tabwidget)
case CT_TabWidget:
if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
// TabWidget corresponds to Page
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Page, opt->state);
if (!resolved.isValid())
break;
const auto &m = *resolved.metrics;
const auto bgSize = m.bgImplicitSize.grownBy(m.margins);
const auto contentSizeWithPadding = contentsSize.grownBy(m.padding);
return contentSizeWithPadding.expandedTo(bgSize);
}
break;
#endif // QT_CONFIG(tabwidget)
#if QT_CONFIG(groupbox)
case CT_GroupBox:
if (const auto *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::GroupBox,
opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const QSize bgSize = metrics.bgImplicitSize
+ QSize(metrics.margins.left() + metrics.margins.right(),
metrics.margins.top() + metrics.margins.bottom());
const QSize contentSize = contentsSize.grownBy(metrics.padding);
const QSize textSize = opt->fontMetrics
.size(Qt::TextShowMnemonic, groupBox->text)
.grownBy(metrics.textPadding);
return textSize.expandedTo(contentSize).expandedTo(bgSize);
}
break;
#endif // QT_CONFIG(groupbox)
#if QT_CONFIG(scrollbar)
case CT_ScrollBar:
if (qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ScrollBar, opt->state);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const int iH = metrics.indicatorImplicitSize.height()
+ metrics.padding.top() + metrics.padding.bottom()
+ metrics.indicatorMargins.top() + metrics.indicatorMargins.bottom();
const int iW = metrics.indicatorImplicitSize.width()
+ metrics.padding.left() + metrics.padding.right()
+ metrics.indicatorMargins.left() + metrics.indicatorMargins.right();
const int bgH = metrics.bgImplicitSize.height()
+ metrics.margins.top() + metrics.margins.bottom();
const int bgW = metrics.bgImplicitSize.width()
+ metrics.margins.left() + metrics.margins.right();
return QSize(std::max(iW, bgW), std::max(iH, bgH));
}
break;
#endif // QT_CONFIG(scrollbar)
#if QT_CONFIG(menu)
case CT_MenuItem:
if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
// The menu dictates the item width
// QMenu uses for PM_MenuHMargin for both left/right paddings,
// so the available width is bgImplicitWidth + margins - 2 * padding.left.
const auto menuType = widget && widget->inherits("QComboBoxPrivateContainer")
? QQStyleKitReader::ControlType::Popup
: QQStyleKitReader::ControlType::Menu;
const auto menuResolved = d->resolveLayout(menuType, opt->state);
int minMenuW = 0;
if (menuResolved.isValid()) {
const auto &mm = *menuResolved.metrics;
minMenuW = mm.bgImplicitSize.width()
+ mm.margins.left() + mm.margins.right() - 2 * mm.padding.left();
}
if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuSeparator,
opt->state);
if (!resolved.isValid())
break;
const auto &m = *resolved.metrics;
const int bgWidth = m.bgImplicitSize.width()
+ m.margins.left() + m.margins.right();
const int bgHeight = m.bgImplicitSize.height()
+ m.margins.top() + m.margins.bottom();
return QSize(std::max(bgWidth, minMenuW), bgHeight);
}
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuItem, opt->state);
if (!resolved.isValid())
break;
const auto &m = *resolved.metrics;
const auto *indicator = resolved.indicator();
const auto *first = indicator ? indicator->first() : nullptr;
const auto *second = indicator ? indicator->second() : nullptr;
int indicatorW = 0;
int indicatorH = 0;
using QtPrivate::qSaturateRound;
if (first && menuItem->checkType != QStyleOptionMenuItem::NotCheckable) {
indicatorW += qSaturateRound(
first->leftMargin() + first->width() + first->rightMargin()) + m.spacing;
indicatorH = std::max(
qSaturateRound(first->topMargin() + first->bottomMargin() + first->height()),
indicatorH);
}
if (second && menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {
indicatorW += qSaturateRound(
second->leftMargin() + second->width() + second->rightMargin()) + m.spacing;
indicatorH = std::max(
qSaturateRound(second->topMargin() + second->bottomMargin() + second->height()),
indicatorH);
}
const int bgH = m.bgImplicitSize.height() + m.margins.top() + m.margins.bottom();
const int iconReserved = menuItem->maxIconWidth > 0
? menuItem->maxIconWidth + m.spacing
: 0;
const int contentW = contentsSize.width()
+ m.padding.left() + m.padding.right()
+ m.textPadding.left() + m.textPadding.right()
+ indicatorW + iconReserved;
const int contentH = contentsSize.height()
+ m.padding.top() + m.padding.bottom()
+ m.textPadding.top() + m.textPadding.bottom();
return QSize(std::max(contentW, minMenuW), std::max({ contentH, indicatorH, bgH }));
}
break;
case CT_Menu: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::Menu, opt->state);
if (!resolved.isValid())
break;
const auto &m = *resolved.metrics;
const int height = std::max(contentsSize.height(), m.bgImplicitSize.height())
+ m.margins.top() + m.margins.bottom();
return QSize(contentsSize.width(), height);
}
#endif // QT_CONFIG(menu)
#if QT_CONFIG(menubar)
case CT_MenuBar: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuBar, opt->state);
if (!resolved.isValid())
break;
const auto &m = *resolved.metrics;
const int bgWidth = m.bgImplicitSize.width() + m.margins.left() + m.margins.right();
const int bgHeight = m.bgImplicitSize.height() + m.margins.top() + m.margins.bottom();
const int contentW = contentsSize.width() + m.padding.left() + m.padding.right();
const int contentH = contentsSize.height() + m.padding.top() + m.padding.bottom();
return QSize(std::max(contentW, bgWidth), std::max(contentH, bgHeight));
}
case CT_MenuBarItem: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuBarItem,
opt->state);
if (!resolved.isValid())
break;
const auto &m = *resolved.metrics;
const int bgWidth = m.bgImplicitSize.width() + m.margins.left() + m.margins.right();
const int bgHeight = m.bgImplicitSize.height() + m.margins.top() + m.margins.bottom();
const int contentW = contentsSize.width()
+ m.padding.left() + m.padding.right()
+ m.textPadding.left() + m.textPadding.right();
const int contentH = contentsSize.height()
+ m.padding.top() + m.padding.bottom()
+ m.textPadding.top() + m.textPadding.bottom();
return QSize(std::max(contentW, bgWidth), std::max(contentH, bgHeight));
}
#endif // QT_CONFIG(menubar)
default:
break;
}
return QCommonStyle::sizeFromContents(ct, opt, contentsSize, widget);
}
/*! \reimp */
int QStyleKitStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *widget) const
{
Q_D(const QStyleKitStyle);
switch (m) {
#if QT_CONFIG(menu)
case PM_MenuPanelWidth:
// The panel border is drawn as part of the resolved background (see PE_PanelMenu);
return 0;
case PM_MenuTearoffHeight:
case PM_MenuScrollerHeight:
return 0;
case PM_MenuHMargin:
case PM_MenuVMargin: {
const auto controlType = widget && widget->inherits("QComboBoxPrivateContainer")
? QQStyleKitReader::ControlType::Popup
: QQStyleKitReader::ControlType::Menu;
const auto resolved = d->resolveLayout(controlType, opt ? opt->state : QStyle::State_None);
if (!resolved.isValid())
break;
return m == PM_MenuHMargin ? resolved.metrics->padding.left()
: resolved.metrics->padding.top();
}
#endif
#if QT_CONFIG(menubar)
case PM_MenuBarItemSpacing: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::MenuBarItem,
opt ? opt->state : QStyle::State_None);
if (resolved.isValid())
return resolved.metrics->spacing;
break;
}
#endif
#if QT_CONFIG(tooltip)
case PM_ToolTipLabelFrameWidth: {
// QTipLabel adds 1 to this and applies it uniformly on all sides
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolTip,
opt ? opt->state : QStyle::State_None);
if (!resolved.isValid())
break;
const auto *m = resolved.metrics;
return qMax(0, m->margins.left() + m->padding.left() + m->textPadding.left() - 1);
}
#endif
#if QT_CONFIG(toolbar)
case PM_ToolBarFrameWidth:
// The frame is folded into the resolved background/margins (see CE_ToolBar);
return 0;
case PM_ToolBarItemMargin: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolBar,
opt ? opt->state : QStyle::State_None);
if (!resolved.isValid())
break;
return resolved.metrics->padding.left();
}
case PM_ToolBarItemSpacing: {
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolBar,
opt ? opt->state : QStyle::State_None);
if (resolved.isValid())
return resolved.metrics->spacing;
break;
}
case PM_ToolBarHandleExtent: {
// The only lever available to honor the style's background width/height
// QToolBarLayout uses this to calculate the minimum size of the toolbar
const auto resolved = d->resolveLayout(QQStyleKitReader::ControlType::ToolBar,
opt ? opt->state : QStyle::State_None);
if (!resolved.isValid())
break;
const auto &metrics = *resolved.metrics;
const bool horizontal = !opt || (opt->state & State_Horizontal);
const int crossImplicit = horizontal
? metrics.bgImplicitSize.height()
: metrics.bgImplicitSize.width();
const int crossMargins = horizontal
? metrics.margins.top() + metrics.margins.bottom()
: metrics.margins.left() + metrics.margins.right();
const int crossPadding = horizontal
? metrics.padding.top() + metrics.padding.bottom()
: metrics.padding.left() + metrics.padding.right();
return qMax(0, crossImplicit + crossMargins - crossPadding);
}
#endif // QT_CONFIG(toolbar)
#if QT_CONFIG(tabbar)
case PM_TabBarTabHSpace:
case PM_TabBarTabVSpace:
case PM_TabBarTabOverlap:
// Not supported in StyleKit
case PM_TabCloseIndicatorWidth:
case PM_TabCloseIndicatorHeight:
return 0;
#endif // QT_CONFIG(tabbar)
default:
break;
}
return QCommonStyle::pixelMetric(m, opt, widget);
}
/*! \reimp */
int QStyleKitStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
QStyleHintReturn *shret) const
{
switch (sh) {
// keep consistent with the Controls style behavior
case SH_SpinBox_SelectOnStep:
case SH_ToolBar_Movable:
case SH_TabBar_PreferNoArrows:
return 0;
case SH_Menu_MouseTracking:
case SH_MenuBar_MouseTracking:
case SH_Menu_Scrollable:
return 1;
default:
break;
}
return QCommonStyle::styleHint(sh, opt, w, shret);
}
/*! \reimp */
QPalette QStyleKitStyle::standardPalette() const
{
return QCommonStyle::standardPalette();
}
/*! \reimp */
void QStyleKitStyle::polish(QWidget *widget)
{
if (!widget)
return;
Q_D(QStyleKitStyle);
// When no user style is loaded, create the empty fallback style now,
// before any reader is created, so that all widgets are styled consistently
if (!d->style)
d->ensureDefaultStyle();
widget->setAttribute(Qt::WA_Hover);
#if QT_CONFIG(scrollbar)
// QScrollBar sets WA_OpaquePaintEvent in its constructor, which skips
// background erase before paint. This can leave artifacts when the
// style's background is invisible, so disable it
if (qobject_cast<QScrollBar *>(widget))
widget->setAttribute(Qt::WA_OpaquePaintEvent, false);
#endif
// Create per-widget reader for interactive controls (transitions)
const bool isInteractiveControl = false
#if QT_CONFIG(pushbutton)
|| qobject_cast<const QPushButton *>(widget)
#endif
#if QT_CONFIG(checkbox)
|| qobject_cast<const QCheckBox *>(widget)
#endif
#if QT_CONFIG(radiobutton)
|| qobject_cast<const QRadioButton *>(widget)
#endif
#if QT_CONFIG(combobox)
|| qobject_cast<const QComboBox *>(widget)
|| (widget && widget->inherits("QComboBoxPrivateContainer"))
#endif
#if QT_CONFIG(lineedit)
|| qobject_cast<const QLineEdit *>(widget)
#endif
#if QT_CONFIG(textedit)
|| qobject_cast<const QTextEdit *>(widget)
|| qobject_cast<const QPlainTextEdit *>(widget)
#endif
#if QT_CONFIG(label)
|| qobject_cast<const QLabel *>(widget)
#endif
#if QT_CONFIG(progressbar)
|| qobject_cast<const QProgressBar *>(widget)
#endif
#if QT_CONFIG(slider)
|| qobject_cast<const QSlider *>(widget)
#endif
#if QT_CONFIG(dial)
|| qobject_cast<const QDial *>(widget)
#endif
#if QT_CONFIG(scrollbar)
|| qobject_cast<const QScrollBar *>(widget)
#endif
#if QT_CONFIG(spinbox)
|| qobject_cast<const QSpinBox *>(widget)
#endif
#if QT_CONFIG(tabbar)
|| qobject_cast<const QTabBar *>(widget)
#endif
#if QT_CONFIG(tabwidget)
|| qobject_cast<const QTabWidget *>(widget)
#endif
#if QT_CONFIG(menu)
|| qobject_cast<const QMenu *>(widget)
|| qobject_cast<const QMenuBar *>(widget)
#endif
;
if (isInteractiveControl)
d->readerForWidget(widget);
d->setStyleAutoFill(widget);
d->setStyleTranslucency(widget);
#if QT_CONFIG(lineedit)
if (auto *lineEdit = qobject_cast<QLineEdit *>(widget)) {
if (!lineEdit->property("_q_stylekit_alignment_set").toBool()) {
QQStyleKitReader *r = d->readerForWidget(widget);
if (r) {
const QWidget *target = containerWidget(widget);
QQStyleKitReader::ControlType ct = controlTypeForWidget(target);
r->setControlType(ct);
const auto *textProps = r->global()->text();
if (textProps) {
const uint align = resolvedAlignment(
textProps->alignment(), Qt::AlignLeft, Qt::AlignVCenter);
lineEdit->setAlignment(Qt::Alignment(align));
}
}
}
}
#endif
d->refreshStyleFont(widget);
d->refreshStylePalette(widget);
d->refreshStyleMinimumSize(widget);
bool needsEventFilter = isSelfPaintingWidget(widget);
#if QT_CONFIG(menu)
// Menus are transient: drop their sub-element tracking when they hide
needsEventFilter = needsEventFilter || qobject_cast<QMenu *>(widget);
#endif
if (needsEventFilter)
widget->installEventFilter(this);
QCommonStyle::polish(widget);
}
/*! \reimp */
void QStyleKitStyle::polish(QApplication *app)
{
QCommonStyle::polish(app);
}
/*! \reimp */
void QStyleKitStyle::polish(QPalette &palette)
{
QCommonStyle::polish(palette);
}
/*! \reimp */
void QStyleKitStyle::unpolish(QWidget *widget)
{
Q_D(QStyleKitStyle);
d->unsetStylePalette(widget);
d->unsetStyleFont(widget);
d->unsetStyleMinimumSize(widget);
bool hasEventFilter = isSelfPaintingWidget(widget);
#if QT_CONFIG(menu)
hasEventFilter = hasEventFilter || qobject_cast<QMenu *>(widget);
#endif
if (hasEventFilter)
widget->removeEventFilter(this);
d->unsetStyleAutoFill(widget);
d->unsetStyleTranslucency(widget);
d->cleanupWidgetReader(widget);
#if QT_CONFIG(scrollbar)
if (qobject_cast<QScrollBar *>(widget))
widget->setAttribute(Qt::WA_OpaquePaintEvent);
#endif
QCommonStyle::unpolish(widget);
}
/*! \reimp */
void QStyleKitStyle::unpolish(QApplication *app)
{
Q_D(QStyleKitStyle);
d->clearMetricsCache();
QCommonStyle::unpolish(app);
}
/*! \reimp */
bool QStyleKitStyle::eventFilter(QObject *obj, QEvent *event)
{
Q_D(QStyleKitStyle);
switch (event->type()) {
case QEvent::EnabledChange:
case QEvent::HoverEnter:
case QEvent::HoverLeave:
case QEvent::FocusIn:
case QEvent::FocusOut:
if (auto *w = qobject_cast<QWidget *>(obj)) {
if (d->customPaletteWidgets.contains(w))
d->refreshStylePalette(w);
if (d->customFontWidgets.contains(w))
d->refreshStyleFont(w);
}
break;
#if QT_CONFIG(menu)
case QEvent::Hide:
if (auto *menu = qobject_cast<QMenu *>(obj))
d->cleanupSubElements(menu);
break;
#endif
default:
break;
}
return QCommonStyle::eventFilter(obj, event);
}
/*! \reimp */
bool QStyleKitStyle::event(QEvent *event)
{
return QCommonStyle::event(event);
}
QT_END_NAMESPACE
#include "moc_qstylekitstyle.cpp"