File indexing completed on 2024-04-14 03:58:05

0001 /*
0002     SPDX-FileCopyrightText: 2016 The Qt Company Ltd. <https://www.qt.io/licensing/>
0003     SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
0004     SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
0005     SPDX-FileCopyrightText: 2019 David Redondo <david@david-redondo.de>
0006     SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0007 
0008     SPDX-License-Identifier: LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KFQF-Accepted-GPL OR LicenseRef-Qt-Commercial
0009 */
0010 
0011 #include "kquickstyleitem_p.h"
0012 
0013 #include <QApplication>
0014 #include <QPainter>
0015 #include <QPixmapCache>
0016 #include <QQuickWindow>
0017 #include <QStringBuilder>
0018 #include <QStyle>
0019 #include <QStyleFactory>
0020 #include <qsgninepatchnode.h>
0021 #include <qstyleoption.h>
0022 
0023 #include <KConfigGroup>
0024 #include <ksharedconfig.h>
0025 
0026 #include <Kirigami/Platform/PlatformTheme>
0027 #include <Kirigami/Platform/TabletModeWatcher>
0028 
0029 std::unique_ptr<QStyle> KQuickStyleItem::s_style = nullptr;
0030 
0031 QStyle *KQuickStyleItem::style()
0032 {
0033     if (s_style) {
0034         return s_style.get();
0035     } else {
0036         return qApp->style();
0037     }
0038 }
0039 
0040 static bool isKeyFocusReason(Qt::FocusReason reason)
0041 {
0042     return reason == Qt::TabFocusReason || reason == Qt::BacktabFocusReason || reason == Qt::ShortcutFocusReason;
0043 }
0044 
0045 KQuickStyleItem::KQuickStyleItem(QQuickItem *parent)
0046     : QQuickItem(parent)
0047     , m_styleoption(nullptr)
0048     , m_itemType(Undefined)
0049     , m_sunken(false)
0050     , m_raised(false)
0051     , m_flat(false)
0052     , m_active(true)
0053     , m_selected(false)
0054     , m_focus(false)
0055     , m_hover(false)
0056     , m_on(false)
0057     , m_horizontal(true)
0058     , m_transient(false)
0059     , m_sharedWidget(false)
0060     , m_minimum(0)
0061     , m_maximum(100)
0062     , m_value(0)
0063     , m_step(0)
0064     , m_paintMargins(0)
0065     , m_contentWidth(0)
0066     , m_contentHeight(0)
0067     , m_textureWidth(0)
0068     , m_textureHeight(0)
0069     , m_focusReason(Qt::NoFocusReason)
0070 {
0071     // Check that we really are a QApplication before attempting to access the
0072     // application style.
0073     //
0074     // Functions used in this file which are safe to access through qApp are:
0075     //
0076     //   qApp->font() and qApp->fontChanged() - provided by QGuiApplication
0077     //   qApp->font("classname") - provided by QApplication but safe
0078     //   qApp->layoutDirection() - provided by QGuiApplication
0079     //   qApp->wheelScrollLines() - uses styleHints() provided by QGuiApplication
0080     //   qApp->testAttribute() and qApp->setAttribute() - provided by QCoreApplication
0081     //   qApp->devicePixelRatio() - provided by QGuiApplication
0082     //   qApp->palette("classname") - provided by QApplication but safe
0083     //
0084     // but any use of qApp->style(), even if only trying to test that it exists,
0085     // will assert.  Use KQuickStyleItem::style() as above.
0086     //
0087     // Any use of any other function provided by QApplication must either be checked
0088     // to ensure that it is safe to use if not a QApplication, or guarded.
0089 
0090     if (qobject_cast<QApplication *>(QCoreApplication::instance())) {
0091         // We are a QApplication.  Unfortunately there is no styleChanged signal
0092         // available, and it only sends QEvent::StyleChange events to all QWidgets.
0093         // So watch for the existing application style being destroyed, which means
0094         // that a new one is being applied.  See QApplication::setStyle().
0095         QStyle *style = qApp->style();
0096         if (style) {
0097             connect(style, &QObject::destroyed, this, &KQuickStyleItem::styleChanged);
0098         }
0099     } else if (!s_style) {
0100         // We are not a QApplication.  Create an internal copy of the configured
0101         // desktop style, to be used for metrics, options and painting.
0102         KSharedConfig::Ptr kdeglobals = KSharedConfig::openConfig();
0103         KConfigGroup cg(kdeglobals, QStringLiteral("KDE"));
0104         const QString defaultStyleName = QStringLiteral("Fusion");
0105         s_style.reset(QStyleFactory::create(cg.readEntry("widgetStyle", defaultStyleName)));
0106         if (!s_style) {
0107             s_style.reset(QStyleFactory::create(defaultStyleName));
0108             // Prevent inevitable crashes on nullptr dereference
0109             if (!s_style) {
0110                 qWarning() << "org.kde.desktop: Could not find any QStyle such as Breeze or Fusion";
0111                 ::exit(EXIT_FAILURE);
0112             }
0113         }
0114     }
0115 
0116     m_font = qApp->font();
0117     setFlag(QQuickItem::ItemHasContents, true);
0118     setSmooth(false);
0119 
0120     qGuiApp->installEventFilter(this);
0121 
0122     Kirigami::Platform::TabletModeWatcher::self()->addWatcher(this);
0123 }
0124 
0125 KQuickStyleItem::~KQuickStyleItem()
0126 {
0127     if (const QStyleOptionButton *aux = qstyleoption_cast<const QStyleOptionButton *>(m_styleoption)) {
0128         delete aux;
0129     } else if (const QStyleOptionViewItem *aux = qstyleoption_cast<const QStyleOptionViewItem *>(m_styleoption)) {
0130         delete aux;
0131     } else if (const QStyleOptionHeader *aux = qstyleoption_cast<const QStyleOptionHeader *>(m_styleoption)) {
0132         delete aux;
0133     } else if (const QStyleOptionToolButton *aux = qstyleoption_cast<const QStyleOptionToolButton *>(m_styleoption)) {
0134         delete aux;
0135     } else if (const QStyleOptionToolBar *aux = qstyleoption_cast<const QStyleOptionToolBar *>(m_styleoption)) {
0136         delete aux;
0137     } else if (const QStyleOptionTab *aux = qstyleoption_cast<const QStyleOptionTab *>(m_styleoption)) {
0138         delete aux;
0139     } else if (const QStyleOptionFrame *aux = qstyleoption_cast<const QStyleOptionFrame *>(m_styleoption)) {
0140         delete aux;
0141     } else if (const QStyleOptionFocusRect *aux = qstyleoption_cast<const QStyleOptionFocusRect *>(m_styleoption)) {
0142         delete aux;
0143     } else if (const QStyleOptionTabWidgetFrame *aux = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(m_styleoption)) {
0144         delete aux;
0145     } else if (const QStyleOptionMenuItem *aux = qstyleoption_cast<const QStyleOptionMenuItem *>(m_styleoption)) {
0146         delete aux;
0147     } else if (const QStyleOptionComboBox *aux = qstyleoption_cast<const QStyleOptionComboBox *>(m_styleoption)) {
0148         delete aux;
0149     } else if (const QStyleOptionSpinBox *aux = qstyleoption_cast<const QStyleOptionSpinBox *>(m_styleoption)) {
0150         delete aux;
0151     } else if (const QStyleOptionSlider *aux = qstyleoption_cast<const QStyleOptionSlider *>(m_styleoption)) {
0152         delete aux;
0153     } else if (const QStyleOptionProgressBar *aux = qstyleoption_cast<const QStyleOptionProgressBar *>(m_styleoption)) {
0154         delete aux;
0155     } else if (const QStyleOptionGroupBox *aux = qstyleoption_cast<const QStyleOptionGroupBox *>(m_styleoption)) {
0156         delete aux;
0157     } else {
0158         delete m_styleoption;
0159     }
0160 
0161     m_styleoption = nullptr;
0162     Kirigami::Platform::TabletModeWatcher::self()->removeWatcher(this);
0163 }
0164 
0165 void KQuickStyleItem::initStyleOption()
0166 {
0167     if (!m_theme) {
0168         m_theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
0169         Q_ASSERT(m_theme);
0170 
0171         connect(m_theme, &Kirigami::Platform::PlatformTheme::colorsChanged, this, [this]() {
0172             // we need to reset the palette event if Qt::AA_SetPalette attribute has been set
0173             m_styleoption->palette = m_theme->palette();
0174             polish();
0175         });
0176     }
0177     Q_ASSERT(m_theme);
0178 
0179     if (m_styleoption) {
0180         m_styleoption->state = {};
0181     }
0182 
0183     QString sizeHint = m_hints.value(QStringLiteral("size")).toString();
0184 
0185     bool needsResolvePalette = true;
0186     bool preventMirroring = false;
0187 
0188     switch (m_itemType) {
0189     case Button: {
0190         if (!m_styleoption) {
0191             m_styleoption = new QStyleOptionButton();
0192         }
0193 
0194         QStyleOptionButton *opt = qstyleoption_cast<QStyleOptionButton *>(m_styleoption);
0195         opt->text = text();
0196 
0197         if (m_iconDirty || opt->icon.isNull()) {
0198             opt->icon = iconFromIconProperty();
0199             m_iconDirty = false;
0200         }
0201 
0202         auto iconSize = QSize(m_properties[QStringLiteral("iconWidth")].toInt(), m_properties[QStringLiteral("iconHeight")].toInt());
0203         if (iconSize.isEmpty()) {
0204             int e = KQuickStyleItem::style()->pixelMetric(QStyle::PM_ButtonIconSize, m_styleoption, nullptr);
0205             if (iconSize.width() <= 0) {
0206                 iconSize.setWidth(e);
0207             }
0208             if (iconSize.height() <= 0) {
0209                 iconSize.setHeight(e);
0210             }
0211         }
0212         opt->iconSize = iconSize;
0213         opt->features = activeControl() == QLatin1String("default") ? QStyleOptionButton::DefaultButton : QStyleOptionButton::None;
0214         if (m_flat) {
0215             opt->features |= QStyleOptionButton::Flat;
0216         }
0217         const QFont font = qApp->font("QPushButton");
0218         opt->fontMetrics = QFontMetrics(font);
0219         bool hasMenu = m_properties[QStringLiteral("menu")].toBool();
0220         if (hasMenu) {
0221             opt->features |= QStyleOptionButton::HasMenu;
0222         }
0223         break;
0224     }
0225     case ItemRow: {
0226         if (!m_styleoption) {
0227             m_styleoption = new QStyleOptionViewItem();
0228         }
0229 
0230         QStyleOptionViewItem *opt = qstyleoption_cast<QStyleOptionViewItem *>(m_styleoption);
0231         opt->features = {};
0232         if (activeControl() == QLatin1String("alternate")) {
0233             opt->features |= QStyleOptionViewItem::Alternate;
0234         }
0235         break;
0236     }
0237 
0238     case Splitter: {
0239         if (!m_styleoption) {
0240             m_styleoption = new QStyleOption;
0241         }
0242         break;
0243     }
0244 
0245     case Item: {
0246         if (!m_styleoption) {
0247             m_styleoption = new QStyleOptionViewItem();
0248         }
0249         QStyleOptionViewItem *opt = qstyleoption_cast<QStyleOptionViewItem *>(m_styleoption);
0250         opt->features = QStyleOptionViewItem::HasDisplay;
0251         opt->text = text();
0252         opt->textElideMode = Qt::ElideRight;
0253         opt->displayAlignment = Qt::AlignLeft | Qt::AlignVCenter;
0254         opt->decorationAlignment = Qt::AlignCenter;
0255         resolvePalette();
0256         needsResolvePalette = false;
0257         QPalette pal = m_styleoption->palette;
0258         pal.setBrush(QPalette::Base, Qt::NoBrush);
0259         m_styleoption->palette = pal;
0260         const QFont font = qApp->font("QAbstractItemView");
0261         opt->font = font;
0262         opt->fontMetrics = QFontMetrics(font);
0263         break;
0264     }
0265     case ItemBranchIndicator: {
0266         if (!m_styleoption) {
0267             m_styleoption = new QStyleOption;
0268         }
0269 
0270         if (m_properties.value(QStringLiteral("isItem")).toBool()) {
0271             m_styleoption->state = QStyle::State_Item;
0272         }
0273         if (m_properties.value(QStringLiteral("hasChildren")).toBool()) {
0274             m_styleoption->state |= QStyle::State_Children;
0275         }
0276         if (m_properties.value(QStringLiteral("hasSibling")).toBool()) { // Even this one could go away
0277             m_styleoption->state |= QStyle::State_Sibling;
0278         }
0279         if (m_on) {
0280             m_styleoption->state |= QStyle::State_Open;
0281         }
0282         break;
0283     }
0284     case Header: {
0285         if (!m_styleoption) {
0286             m_styleoption = new QStyleOptionHeader();
0287         }
0288 
0289         QStyleOptionHeader *opt = qstyleoption_cast<QStyleOptionHeader *>(m_styleoption);
0290         opt->text = text();
0291         opt->textAlignment = static_cast<Qt::AlignmentFlag>(m_properties.value(QStringLiteral("textalignment")).toInt());
0292         opt->sortIndicator = activeControl() == QLatin1String("down") ? QStyleOptionHeader::SortDown
0293             : activeControl() == QLatin1String("up")                  ? QStyleOptionHeader::SortUp
0294                                                                       : QStyleOptionHeader::None;
0295         QString headerpos = m_properties.value(QStringLiteral("headerpos")).toString();
0296         if (headerpos == QLatin1String("beginning")) {
0297             opt->position = QStyleOptionHeader::Beginning;
0298         } else if (headerpos == QLatin1String("end")) {
0299             opt->position = QStyleOptionHeader::End;
0300         } else if (headerpos == QLatin1String("only")) {
0301             opt->position = QStyleOptionHeader::OnlyOneSection;
0302         } else {
0303             opt->position = QStyleOptionHeader::Middle;
0304         }
0305         opt->orientation = m_properties.value(QStringLiteral("orientation")).value<Qt::Orientation>();
0306         if (opt->orientation == Qt::Orientation(0)) {
0307             opt->orientation = Qt::Horizontal;
0308         }
0309         const QFont font = qApp->font("QHeaderView");
0310         opt->fontMetrics = QFontMetrics(font);
0311         break;
0312     }
0313     case DelayButton:
0314     case ToolButton: {
0315         if (!m_styleoption) {
0316             m_styleoption = new QStyleOptionToolButton();
0317         }
0318 
0319         QStyleOptionToolButton *opt = qstyleoption_cast<QStyleOptionToolButton *>(m_styleoption);
0320         opt->subControls = QStyle::SC_ToolButton;
0321 
0322         if (m_flat) {
0323             opt->state |= QStyle::State_AutoRaise;
0324         }
0325 
0326         opt->activeSubControls = QStyle::SC_ToolButton;
0327         opt->text = text();
0328 
0329         if (m_iconDirty || opt->icon.isNull()) {
0330             opt->icon = iconFromIconProperty();
0331             m_iconDirty = false;
0332         }
0333 
0334         auto iconSize = QSize(m_properties[QStringLiteral("iconWidth")].toInt(), m_properties[QStringLiteral("iconHeight")].toInt());
0335         if (iconSize.isEmpty()) {
0336             const auto metric = m_flat ? QStyle::PM_ToolBarIconSize : QStyle::PM_ButtonIconSize;
0337             int e = KQuickStyleItem::style()->pixelMetric(metric, m_styleoption, nullptr);
0338             if (iconSize.width() <= 0) {
0339                 iconSize.setWidth(e);
0340             }
0341             if (iconSize.height() <= 0) {
0342                 iconSize.setHeight(e);
0343             }
0344         }
0345         opt->iconSize = iconSize;
0346 
0347         if (m_properties.value(QStringLiteral("menu")).toBool()) {
0348             opt->features |= QStyleOptionToolButton::HasMenu;
0349         }
0350 
0351         const int toolButtonStyle = m_properties.value(QStringLiteral("toolButtonStyle")).toInt();
0352 
0353         switch (toolButtonStyle) {
0354         case Qt::ToolButtonIconOnly:
0355         case Qt::ToolButtonTextOnly:
0356         case Qt::ToolButtonTextBesideIcon:
0357         case Qt::ToolButtonTextUnderIcon:
0358         case Qt::ToolButtonFollowStyle:
0359             opt->toolButtonStyle = (Qt::ToolButtonStyle)toolButtonStyle;
0360             break;
0361         default:
0362             opt->toolButtonStyle = Qt::ToolButtonFollowStyle;
0363         }
0364 
0365         const QFont font = qApp->font("QToolButton");
0366         opt->font = font;
0367         opt->fontMetrics = QFontMetrics(font);
0368         break;
0369     }
0370     case ToolBar: {
0371         if (!m_styleoption) {
0372             m_styleoption = new QStyleOptionToolBar();
0373         }
0374         break;
0375     }
0376     case Tab: {
0377         if (!m_styleoption) {
0378             m_styleoption = new QStyleOptionTab();
0379         }
0380 
0381         QStyleOptionTab *opt = qstyleoption_cast<QStyleOptionTab *>(m_styleoption);
0382         opt->text = text();
0383 
0384         if (m_iconDirty || opt->icon.isNull()) {
0385             opt->icon = iconFromIconProperty();
0386             m_iconDirty = false;
0387         }
0388 
0389         auto iconSize = QSize(m_properties[QStringLiteral("iconWidth")].toInt(), m_properties[QStringLiteral("iconHeight")].toInt());
0390         if (iconSize.isEmpty()) {
0391             int e = KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarIconSize, m_styleoption, nullptr);
0392             if (iconSize.width() <= 0) {
0393                 iconSize.setWidth(e);
0394             }
0395             if (iconSize.height() <= 0) {
0396                 iconSize.setHeight(e);
0397             }
0398         }
0399         opt->iconSize = iconSize;
0400 
0401         if (m_properties.value(QStringLiteral("hasFrame")).toBool()) {
0402             opt->features |= QStyleOptionTab::HasFrame;
0403         }
0404 
0405         QString orientation = m_properties.value(QStringLiteral("orientation")).toString();
0406         QString position = m_properties.value(QStringLiteral("tabpos")).toString();
0407         QString selectedPosition = m_properties.value(QStringLiteral("selectedpos")).toString();
0408 
0409         opt->shape = orientation == QLatin1String("Bottom") ? QTabBar::RoundedSouth : QTabBar::RoundedNorth;
0410         if (position == QLatin1String("beginning")) {
0411             opt->position = QStyleOptionTab::Beginning;
0412         } else if (position == QLatin1String("end")) {
0413             opt->position = QStyleOptionTab::End;
0414         } else if (position == QLatin1String("only")) {
0415             opt->position = QStyleOptionTab::OnlyOneTab;
0416         } else {
0417             opt->position = QStyleOptionTab::Middle;
0418         }
0419 
0420         if (selectedPosition == QLatin1String("next")) {
0421             opt->selectedPosition = QStyleOptionTab::NextIsSelected;
0422         } else if (selectedPosition == QLatin1String("previous")) {
0423             opt->selectedPosition = QStyleOptionTab::PreviousIsSelected;
0424         } else {
0425             opt->selectedPosition = QStyleOptionTab::NotAdjacent;
0426         }
0427 
0428         break;
0429     }
0430 
0431     case Frame: {
0432         if (!m_styleoption) {
0433             m_styleoption = new QStyleOptionFrame();
0434         }
0435 
0436         QStyleOptionFrame *opt = qstyleoption_cast<QStyleOptionFrame *>(m_styleoption);
0437         opt->frameShape = QFrame::StyledPanel;
0438         opt->lineWidth = KQuickStyleItem::style()->pixelMetric(QStyle::PM_DefaultFrameWidth, m_styleoption, nullptr);
0439         opt->midLineWidth = opt->lineWidth;
0440         break;
0441     }
0442     case FocusRect: {
0443         if (!m_styleoption) {
0444             m_styleoption = new QStyleOptionFocusRect();
0445         }
0446         // Needed on windows
0447         m_styleoption->state |= QStyle::State_KeyboardFocusChange;
0448         break;
0449     }
0450     case TabFrame: {
0451         if (!m_styleoption) {
0452             m_styleoption = new QStyleOptionTabWidgetFrame();
0453         }
0454         QStyleOptionTabWidgetFrame *opt = qstyleoption_cast<QStyleOptionTabWidgetFrame *>(m_styleoption);
0455 
0456         opt->selectedTabRect = m_properties[QStringLiteral("selectedTabRect")].toRect();
0457         opt->shape = m_properties[QStringLiteral("orientation")] == Qt::BottomEdge ? QTabBar::RoundedSouth : QTabBar::RoundedNorth;
0458         if (minimum()) {
0459             opt->selectedTabRect = QRect(value(), 0, minimum(), height());
0460         }
0461         opt->tabBarSize = QSize(minimum(), height());
0462         // oxygen style needs this hack
0463         opt->leftCornerWidgetSize = QSize(value(), 0);
0464         break;
0465     }
0466     case MenuBar:
0467         if (!m_styleoption) {
0468             QStyleOptionMenuItem *menuOpt = new QStyleOptionMenuItem();
0469             menuOpt->menuItemType = QStyleOptionMenuItem::EmptyArea;
0470             m_styleoption = menuOpt;
0471         }
0472 
0473         break;
0474     case MenuBarItem: {
0475         if (!m_styleoption) {
0476             m_styleoption = new QStyleOptionMenuItem();
0477         }
0478 
0479         QStyleOptionMenuItem *opt = qstyleoption_cast<QStyleOptionMenuItem *>(m_styleoption);
0480         opt->text = text();
0481         opt->menuItemType = QStyleOptionMenuItem::Normal;
0482         setProperty("_q_showUnderlined", m_hints[QStringLiteral("showUnderlined")].toBool());
0483 
0484         const QFont font = qApp->font("QMenuBar");
0485         opt->font = font;
0486         opt->fontMetrics = QFontMetrics(font);
0487         m_font = opt->font;
0488         break;
0489     }
0490     case Menu: {
0491         if (!m_styleoption) {
0492             m_styleoption = new QStyleOptionMenuItem();
0493         }
0494         break;
0495     }
0496     case MenuItem:
0497     case ComboBoxItem: {
0498         if (!m_styleoption) {
0499             m_styleoption = new QStyleOptionMenuItem();
0500         }
0501 
0502         QStyleOptionMenuItem *opt = qstyleoption_cast<QStyleOptionMenuItem *>(m_styleoption);
0503         // For GTK style. See below, in setElementType()
0504         setProperty("_q_isComboBoxPopupItem", m_itemType == ComboBoxItem);
0505 
0506         KQuickStyleItem::MenuItemType type = static_cast<KQuickStyleItem::MenuItemType>(m_properties[QStringLiteral("type")].toInt());
0507         if (type == KQuickStyleItem::ScrollIndicatorType) {
0508             int scrollerDirection = m_properties[QStringLiteral("scrollerDirection")].toInt();
0509             opt->menuItemType = QStyleOptionMenuItem::Scroller;
0510             opt->state |= scrollerDirection == Qt::UpArrow ? QStyle::State_UpArrow : QStyle::State_DownArrow;
0511         } else if (type == KQuickStyleItem::SeparatorType) {
0512             opt->menuItemType = QStyleOptionMenuItem::Separator;
0513         } else {
0514             opt->text = text();
0515 
0516             if (type == KQuickStyleItem::MenuType) {
0517                 opt->menuItemType = QStyleOptionMenuItem::SubMenu;
0518             } else {
0519                 opt->menuItemType = QStyleOptionMenuItem::Normal;
0520 
0521                 QString shortcut = m_properties[QStringLiteral("shortcut")].toString();
0522                 if (!shortcut.isEmpty()) {
0523                     opt->text += QLatin1Char('\t') + shortcut;
0524                     opt->reservedShortcutWidth = qMax(opt->reservedShortcutWidth, qRound(textWidth(shortcut)));
0525                 }
0526 
0527                 if (m_properties[QStringLiteral("checkable")].toBool()) {
0528                     opt->checked = on();
0529                     QVariant exclusive = m_properties[QStringLiteral("exclusive")];
0530                     opt->checkType = exclusive.toBool() ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
0531                 }
0532             }
0533 
0534             if (m_iconDirty || opt->icon.isNull()) {
0535                 opt->icon = iconFromIconProperty();
0536                 m_iconDirty = false;
0537             }
0538 
0539             setProperty("_q_showUnderlined", m_hints[QStringLiteral("showUnderlined")].toBool());
0540 
0541             const QFont font = qApp->font(m_itemType == ComboBoxItem ? "QComboMenuItem" : "QMenu");
0542             opt->font = font;
0543             opt->fontMetrics = QFontMetrics(font);
0544             m_font = opt->font;
0545         }
0546         break;
0547     }
0548     case CheckBox:
0549     case RadioButton: {
0550         if (!m_styleoption) {
0551             m_styleoption = new QStyleOptionButton();
0552         }
0553 
0554         QStyleOptionButton *opt = qstyleoption_cast<QStyleOptionButton *>(m_styleoption);
0555         if (!on()) {
0556             opt->state |= QStyle::State_Off;
0557         }
0558         if (m_properties.value(QStringLiteral("partiallyChecked")).toBool()) {
0559             opt->state |= QStyle::State_NoChange;
0560         }
0561         opt->text = text();
0562 
0563         if (m_iconDirty || opt->icon.isNull()) {
0564             opt->icon = iconFromIconProperty();
0565             m_iconDirty = false;
0566         }
0567 
0568         auto iconSize = QSize(m_properties[QStringLiteral("iconWidth")].toInt(), m_properties[QStringLiteral("iconHeight")].toInt());
0569         if (iconSize.isEmpty()) {
0570             int e = KQuickStyleItem::style()->pixelMetric(QStyle::PM_ButtonIconSize, m_styleoption, nullptr);
0571             if (iconSize.width() <= 0) {
0572                 iconSize.setWidth(e);
0573             }
0574             if (iconSize.height() <= 0) {
0575                 iconSize.setHeight(e);
0576             }
0577         }
0578         opt->iconSize = iconSize;
0579         break;
0580     }
0581     case Edit: {
0582         if (!m_styleoption) {
0583             m_styleoption = new QStyleOptionFrame();
0584         }
0585 
0586         QStyleOptionFrame *opt = qstyleoption_cast<QStyleOptionFrame *>(m_styleoption);
0587         opt->lineWidth = qMax(1, KQuickStyleItem::style()->pixelMetric(QStyle::PM_DefaultFrameWidth, m_styleoption, nullptr)); // this must be non zero
0588         break;
0589     }
0590     case ComboBox: {
0591         if (!m_styleoption) {
0592             m_styleoption = new QStyleOptionComboBox();
0593         }
0594 
0595         QStyleOptionComboBox *opt = qstyleoption_cast<QStyleOptionComboBox *>(m_styleoption);
0596 
0597         const QFont font = qApp->font("QPushButton"); // DAVE - QQC1 code does this, but if you look at QComboBox this doesn't make sense
0598         opt->fontMetrics = QFontMetrics(font);
0599         opt->currentText = text();
0600         opt->editable = m_properties[QStringLiteral("editable")].toBool();
0601 
0602         const QVariant icon = m_properties[QStringLiteral("currentIcon")];
0603         if (icon.canConvert<QIcon>()) {
0604             opt->currentIcon = icon.value<QIcon>();
0605         } else if (icon.canConvert<QString>()) {
0606             opt->currentIcon = m_theme->iconFromTheme(icon.value<QString>(), m_properties[QStringLiteral("iconColor")].value<QColor>());
0607         }
0608         auto iconSize = QSize(m_properties[QStringLiteral("iconWidth")].toInt(), m_properties[QStringLiteral("iconHeight")].toInt());
0609         if (iconSize.isEmpty()) {
0610             int e = KQuickStyleItem::style()->pixelMetric(QStyle::PM_ButtonIconSize, m_styleoption, nullptr);
0611             if (iconSize.width() <= 0) {
0612                 iconSize.setWidth(e);
0613             }
0614             if (iconSize.height() <= 0) {
0615                 iconSize.setHeight(e);
0616             }
0617         }
0618         opt->iconSize = iconSize;
0619         opt->frame = !m_flat;
0620 
0621         break;
0622     }
0623     case SpinBox: {
0624         if (!m_styleoption) {
0625             m_styleoption = new QStyleOptionSpinBox();
0626         }
0627 
0628         QStyleOptionSpinBox *opt = qstyleoption_cast<QStyleOptionSpinBox *>(m_styleoption);
0629         opt->frame = true;
0630         if (value() & 0x1) {
0631             opt->activeSubControls = QStyle::SC_SpinBoxUp;
0632         } else if (value() & (1 << 1)) {
0633             opt->activeSubControls = QStyle::SC_SpinBoxDown;
0634         } else {
0635             opt->activeSubControls = QStyle::SC_None;
0636         }
0637         opt->subControls = QStyle::SC_All;
0638         opt->stepEnabled = {};
0639         if (value() & (1 << 2)) {
0640             opt->stepEnabled |= QAbstractSpinBox::StepUpEnabled;
0641         }
0642         if (value() & (1 << 3)) {
0643             opt->stepEnabled |= QAbstractSpinBox::StepDownEnabled;
0644         }
0645         break;
0646     }
0647     case Slider:
0648     case Dial: {
0649         if (!m_styleoption) {
0650             m_styleoption = new QStyleOptionSlider();
0651         }
0652 
0653         QStyleOptionSlider *opt = qstyleoption_cast<QStyleOptionSlider *>(m_styleoption);
0654         opt->orientation = horizontal() ? Qt::Horizontal : Qt::Vertical;
0655         opt->upsideDown = !horizontal();
0656 
0657         int min = minimum();
0658         int max = std::max(min, maximum());
0659 
0660         opt->minimum = min;
0661         opt->maximum = max;
0662         opt->sliderPosition = value();
0663         opt->singleStep = step();
0664 
0665         if (opt->singleStep) {
0666             qreal numOfSteps = (opt->maximum - opt->minimum) / opt->singleStep;
0667             // at least 5 pixels between tick marks
0668             qreal extent = horizontal() ? width() : height();
0669             if (numOfSteps && (extent / numOfSteps < 5)) {
0670                 opt->tickInterval = qRound((5 * numOfSteps / extent) + 0.5) * step();
0671             } else {
0672                 opt->tickInterval = opt->singleStep;
0673             }
0674 
0675         } else { // default Qt-components implementation
0676             opt->tickInterval = opt->maximum != opt->minimum ? 1200 / (opt->maximum - opt->minimum) : 0;
0677         }
0678 
0679         opt->sliderValue = value();
0680         opt->subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
0681         opt->tickPosition = activeControl() == QLatin1String("ticks") ? QSlider::TicksBelow : QSlider::NoTicks;
0682         if (opt->tickPosition != QSlider::NoTicks) {
0683             opt->subControls |= QStyle::SC_SliderTickmarks;
0684         }
0685 
0686         opt->activeSubControls = QStyle::SC_SliderHandle;
0687         break;
0688     }
0689     case ProgressBar: {
0690         if (!m_styleoption) {
0691             m_styleoption = new QStyleOptionProgressBar();
0692         }
0693 
0694         QStyleOptionProgressBar *opt = qstyleoption_cast<QStyleOptionProgressBar *>(m_styleoption);
0695         if (horizontal()) {
0696             opt->state |= QStyle::State_Horizontal;
0697         } else {
0698             opt->state &= ~QStyle::State_Horizontal;
0699         }
0700         opt->minimum = qMax(0, minimum());
0701         opt->maximum = qMax(0, maximum());
0702         opt->progress = value();
0703         break;
0704     }
0705     case GroupBox: {
0706         if (!m_styleoption) {
0707             m_styleoption = new QStyleOptionGroupBox();
0708         }
0709 
0710         QStyleOptionGroupBox *opt = qstyleoption_cast<QStyleOptionGroupBox *>(m_styleoption);
0711         opt->text = text();
0712         opt->lineWidth = KQuickStyleItem::style()->pixelMetric(QStyle::PM_DefaultFrameWidth, m_styleoption, nullptr);
0713         opt->subControls = QStyle::SC_GroupBoxLabel;
0714         opt->features = {};
0715         if (m_properties[QStringLiteral("sunken")].toBool()) { // Qt draws an ugly line here so I ignore it
0716             opt->subControls |= QStyle::SC_GroupBoxFrame;
0717         } else {
0718             opt->features |= QStyleOptionFrame::Flat;
0719         }
0720         if (m_properties[QStringLiteral("checkable")].toBool()) {
0721             opt->subControls |= QStyle::SC_GroupBoxCheckBox;
0722         }
0723 
0724         break;
0725     }
0726     case ScrollBar: {
0727         if (!m_styleoption) {
0728             m_styleoption = new QStyleOptionSlider();
0729         }
0730 
0731         QStyleOptionSlider *opt = qstyleoption_cast<QStyleOptionSlider *>(m_styleoption);
0732         opt->minimum = qMax(0, minimum());
0733         opt->maximum = qMax(0, maximum());
0734         opt->pageStep = qMax(0, int(horizontal() ? width() : height()));
0735         opt->orientation = horizontal() ? Qt::Horizontal : Qt::Vertical;
0736         if (horizontal()) {
0737             // mirrored horizontal scrollbars are not something you want to interact with
0738             preventMirroring = true;
0739         }
0740         opt->sliderPosition = value();
0741         opt->sliderValue = value();
0742         opt->activeSubControls = (activeControl() == QLatin1String("up")) ? QStyle::SC_ScrollBarSubLine
0743             : (activeControl() == QLatin1String("down"))                  ? QStyle::SC_ScrollBarAddLine
0744             : (activeControl() == QLatin1String("handle"))                ? QStyle::SC_ScrollBarSlider
0745             : hover()                                                     ? QStyle::SC_ScrollBarGroove
0746                                                                           : QStyle::SC_None;
0747         if (raised()) {
0748             opt->state |= QStyle::State_On;
0749         }
0750 
0751         opt->subControls = QStyle::SC_All;
0752 
0753         setTransient(KQuickStyleItem::style()->styleHint(QStyle::SH_ScrollBar_Transient, m_styleoption));
0754         break;
0755     }
0756     default:
0757         break;
0758     }
0759 
0760     if (!m_styleoption) {
0761         m_styleoption = new QStyleOption();
0762     }
0763 
0764     if (needsResolvePalette) {
0765         resolvePalette();
0766     }
0767 
0768     m_styleoption->styleObject = this;
0769     const auto mirror = m_control == nullptr ? qApp->layoutDirection() == Qt::RightToLeft : m_control->property("mirrored").toBool();
0770     m_styleoption->direction = (mirror && !preventMirroring) ? Qt::RightToLeft : Qt::LeftToRight;
0771 
0772     int w = m_textureWidth > 0 ? m_textureWidth : width();
0773     int h = m_textureHeight > 0 ? m_textureHeight : height();
0774 
0775     m_styleoption->rect = QRect(m_paintMargins, 0, w - 2 * m_paintMargins, h);
0776 
0777     if (isEnabled()) {
0778         m_styleoption->state |= QStyle::State_Enabled;
0779         m_styleoption->palette.setCurrentColorGroup(QPalette::Active);
0780     } else {
0781         m_styleoption->palette.setCurrentColorGroup(QPalette::Disabled);
0782     }
0783 
0784     if (m_active) {
0785         m_styleoption->state |= QStyle::State_Active;
0786     } else {
0787         m_styleoption->palette.setCurrentColorGroup(QPalette::Inactive);
0788     }
0789 
0790     if (m_sunken) {
0791         m_styleoption->state |= QStyle::State_Sunken;
0792     }
0793     if (!m_sunken || m_raised) {
0794         m_styleoption->state |= QStyle::State_Raised;
0795     }
0796     if (m_selected) {
0797         m_styleoption->state |= QStyle::State_Selected;
0798     }
0799     if (m_focus) {
0800         m_styleoption->state |= QStyle::State_HasFocus;
0801     }
0802     if (m_on) {
0803         m_styleoption->state |= QStyle::State_On;
0804     }
0805     if (m_hover) {
0806         m_styleoption->state |= QStyle::State_MouseOver;
0807     }
0808     if (m_horizontal) {
0809         m_styleoption->state |= QStyle::State_Horizontal;
0810     }
0811 
0812     // some styles don't draw a focus rectangle if
0813     // QStyle::State_KeyboardFocusChange is not set
0814     if (window()) {
0815         if (isKeyFocusReason(m_focusReason)) {
0816             m_styleoption->state |= QStyle::State_KeyboardFocusChange;
0817         }
0818     }
0819 
0820     if (sizeHint == QLatin1String("mini")) {
0821         m_styleoption->state |= QStyle::State_Mini;
0822     } else if (sizeHint == QLatin1String("small")) {
0823         m_styleoption->state |= QStyle::State_Small;
0824     }
0825 
0826 }
0827 
0828 QIcon KQuickStyleItem::iconFromIconProperty() const
0829 {
0830     QIcon icon;
0831     const QVariant iconProperty = m_properties[QStringLiteral("icon")];
0832     switch (iconProperty.userType()) {
0833     case QMetaType::QIcon:
0834         icon = iconProperty.value<QIcon>();
0835         break;
0836     case QMetaType::QUrl:
0837     case QMetaType::QString: {
0838         QString iconSource = iconProperty.toString();
0839         if (iconSource.startsWith(QLatin1String("qrc:/"))) {
0840             iconSource = iconSource.mid(3);
0841         } else if (iconSource.startsWith(QLatin1String("file:/"))) {
0842             iconSource = QUrl(iconSource).toLocalFile();
0843         }
0844         if (iconSource.contains(QLatin1String("/"))) {
0845             icon = QIcon(iconSource);
0846         } else {
0847             icon = m_theme->iconFromTheme(iconSource, m_properties[QStringLiteral("iconColor")].value<QColor>());
0848         }
0849         break;
0850     }
0851     default:
0852         break;
0853     }
0854     return icon;
0855 }
0856 
0857 const char *KQuickStyleItem::classNameForItem() const
0858 {
0859     switch (m_itemType) {
0860     case Button:
0861         return "QPushButton";
0862     case RadioButton:
0863         return "QRadioButton";
0864     case CheckBox:
0865         return "QCheckBox";
0866     case ComboBox:
0867         return "QComboBox";
0868     case ComboBoxItem:
0869         return "QComboMenuItem";
0870     case ToolBar:
0871         return "";
0872     case DelayButton:
0873     case ToolButton:
0874         return "QToolButton";
0875     case Tab:
0876         return "QTabButton";
0877     case TabFrame:
0878         return "QTabBar";
0879     case Edit:
0880         return "QTextEdit";
0881     case GroupBox:
0882         return "QGroupBox";
0883     case Header:
0884         return "QHeaderView";
0885     case Item:
0886     case ItemRow:
0887         return "QAbstractItemView";
0888     case Menu:
0889     case MenuItem:
0890         return "QMenu";
0891     case MenuBar:
0892     case MenuBarItem:
0893         return "QMenuBar";
0894     default:
0895         return "";
0896     }
0897     Q_UNREACHABLE();
0898 }
0899 
0900 void KQuickStyleItem::resolvePalette()
0901 {
0902     if (QCoreApplication::testAttribute(Qt::AA_SetPalette)) {
0903         return;
0904     }
0905 
0906     const QVariant controlPalette = m_control ? m_control->property("palette") : QVariant();
0907     if (controlPalette.isValid()) {
0908         m_styleoption->palette = controlPalette.value<QPalette>();
0909     } else {
0910         m_styleoption->palette = m_theme->palette();
0911     }
0912 }
0913 
0914 int KQuickStyleItem::topPadding() const
0915 {
0916     return padding(Qt::TopEdge);
0917 }
0918 
0919 int KQuickStyleItem::leftPadding() const
0920 {
0921     return padding(Qt::LeftEdge);
0922 }
0923 
0924 int KQuickStyleItem::rightPadding() const
0925 {
0926     return padding(Qt::RightEdge);
0927 }
0928 
0929 int KQuickStyleItem::bottomPadding() const
0930 {
0931     return padding(Qt::BottomEdge);
0932 }
0933 
0934 int KQuickStyleItem::padding(Qt::Edge edge) const
0935 {
0936     if (m_itemType == Frame) {
0937         const QRect cr = KQuickStyleItem::style()->subElementRect(QStyle::SE_ShapedFrameContents, m_styleoption);
0938         switch (edge) {
0939         case Qt::TopEdge:
0940             return cr.top() - m_styleoption->rect.top();
0941         case Qt::LeftEdge:
0942             return cr.left() - m_styleoption->rect.left();
0943         case Qt::RightEdge:
0944             return m_styleoption->rect.right() - cr.right();
0945         case Qt::BottomEdge:
0946             return m_styleoption->rect.bottom() - cr.bottom();
0947         }
0948     }
0949     return 0;
0950 }
0951 
0952 /*
0953  *   Property style
0954  *
0955  *   Returns a simplified style name.
0956  *
0957  *   QMacStyle = "mac"
0958  *   QWindowsXPStyle = "windowsxp"
0959  *   QFusionStyle = "fusion"
0960  */
0961 
0962 QString KQuickStyleItem::styleName() const
0963 {
0964     const QString fullName = QString::fromLatin1(KQuickStyleItem::style()->metaObject()->className());
0965     QStringView shortName = fullName;
0966     if (shortName.startsWith(QLatin1Char('q'), Qt::CaseInsensitive)) {
0967         shortName = shortName.sliced(1);
0968     }
0969     const QLatin1String suffix("style");
0970     if (shortName.endsWith(suffix, Qt::CaseInsensitive)) {
0971         shortName.chop(suffix.length());
0972     }
0973     return shortName.toString().toLower();
0974 }
0975 
0976 QString KQuickStyleItem::hitTest(int px, int py)
0977 {
0978     QStyle::SubControl subcontrol = QStyle::SC_All;
0979     switch (m_itemType) {
0980     case SpinBox: {
0981         subcontrol = KQuickStyleItem::style()->hitTestComplexControl(QStyle::CC_SpinBox,
0982                                                                      qstyleoption_cast<QStyleOptionComplex *>(m_styleoption),
0983                                                                      QPoint(px, py),
0984                                                                      nullptr);
0985         if (subcontrol == QStyle::SC_SpinBoxUp) {
0986             return QStringLiteral("up");
0987         } else if (subcontrol == QStyle::SC_SpinBoxDown) {
0988             return QStringLiteral("down");
0989         }
0990         break;
0991     }
0992 
0993     case Slider: {
0994         subcontrol = KQuickStyleItem::style()->hitTestComplexControl(QStyle::CC_Slider,
0995                                                                      qstyleoption_cast<QStyleOptionComplex *>(m_styleoption),
0996                                                                      QPoint(px, py),
0997                                                                      nullptr);
0998         if (subcontrol == QStyle::SC_SliderHandle) {
0999             return QStringLiteral("handle");
1000         }
1001         break;
1002     }
1003 
1004     case ScrollBar: {
1005         subcontrol = KQuickStyleItem::style()->hitTestComplexControl(QStyle::CC_ScrollBar,
1006                                                                      qstyleoption_cast<QStyleOptionComplex *>(m_styleoption),
1007                                                                      QPoint(px, py),
1008                                                                      nullptr);
1009         switch (subcontrol) {
1010         case QStyle::SC_ScrollBarSlider:
1011             return QStringLiteral("handle");
1012 
1013         case QStyle::SC_ScrollBarSubLine:
1014             return QStringLiteral("up");
1015 
1016         case QStyle::SC_ScrollBarSubPage:
1017             return QStringLiteral("upPage");
1018 
1019         case QStyle::SC_ScrollBarAddLine:
1020             return QStringLiteral("down");
1021 
1022         case QStyle::SC_ScrollBarAddPage:
1023             return QStringLiteral("downPage");
1024 
1025         default:
1026             break;
1027         }
1028         break;
1029     }
1030 
1031     default:
1032         break;
1033     }
1034     return QStringLiteral("none");
1035 }
1036 
1037 QRect KQuickStyleItem::computeBoundingRect(const QList<QRect> &rects)
1038 {
1039     QRegion r;
1040 
1041     for (const auto& rect : rects) {
1042         r = r.united(rect);
1043     }
1044 
1045     return r.boundingRect();
1046 }
1047 
1048 QSize KQuickStyleItem::sizeFromContents(int width, int height)
1049 {
1050     initStyleOption();
1051 
1052     QSize size;
1053     switch (m_itemType) {
1054     case RadioButton:
1055     case CheckBox: {
1056         auto contentType = (m_itemType == RadioButton) ? QStyle::CT_RadioButton : QStyle::CT_CheckBox;
1057         QStyleOptionButton *btn = qstyleoption_cast<QStyleOptionButton *>(m_styleoption);
1058         QSize contentSize = btn->fontMetrics.size(Qt::TextShowMnemonic, btn->text);
1059         if (!btn->icon.isNull()) {
1060             contentSize.setWidth(contentSize.width() + btn->iconSize.width());
1061             contentSize.setHeight(std::max(contentSize.height(), btn->iconSize.height()));
1062         }
1063         size = KQuickStyleItem::style()->sizeFromContents(contentType, m_styleoption, contentSize);
1064         break;
1065     }
1066     case ToolBar:
1067         size = QSize(200, styleName().contains(QLatin1String("windows")) ? 30 : 42);
1068         break;
1069     case DelayButton:
1070     case ToolButton: {
1071         QStyleOptionToolButton *btn = qstyleoption_cast<QStyleOptionToolButton *>(m_styleoption);
1072         int w = 0;
1073         int h = 0;
1074         if (btn->toolButtonStyle != Qt::ToolButtonTextOnly) {
1075             QSize icon = btn->iconSize;
1076             if (btn->toolButtonStyle == Qt::ToolButtonIconOnly || !btn->icon.isNull()) {
1077                 w = icon.width();
1078                 h = icon.height();
1079             }
1080         }
1081         if (btn->toolButtonStyle != Qt::ToolButtonIconOnly) {
1082             QSize textSize = btn->fontMetrics.size(Qt::TextShowMnemonic, btn->text);
1083             textSize.setWidth(textSize.width() + btn->fontMetrics.horizontalAdvance(QLatin1Char(' ')) * 2);
1084             if (btn->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
1085                 h += 4 + textSize.height();
1086                 if (textSize.width() > w) {
1087                     w = textSize.width();
1088                 }
1089             } else if (btn->toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
1090                 w += 4 + textSize.width();
1091                 if (textSize.height() > h) {
1092                     h = textSize.height();
1093                 }
1094             } else { // TextOnly
1095                 w = textSize.width();
1096                 h = textSize.height();
1097             }
1098         }
1099         btn->rect.setSize(QSize(w, h));
1100         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_ToolButton, m_styleoption, QSize(w, h));
1101         break;
1102     }
1103     case Button: {
1104         QStyleOptionButton *btn = qstyleoption_cast<QStyleOptionButton *>(m_styleoption);
1105 
1106         int contentWidth = btn->fontMetrics.boundingRect(btn->text).width();
1107         int contentHeight = btn->fontMetrics.height();
1108 
1109         if (!btn->icon.isNull()) {
1110             //+4 matches a hardcoded value in QStyle and acts as a margin between the icon and the text.
1111             contentWidth += btn->iconSize.width() + 4;
1112             contentHeight = qMax(btn->fontMetrics.height(), btn->iconSize.height());
1113         }
1114 
1115         int newWidth = qMax(width, contentWidth);
1116         int newHeight = qMax(height, contentHeight);
1117         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_PushButton, m_styleoption, QSize(newWidth, newHeight));
1118         break;
1119     }
1120     case ComboBox: {
1121         QStyleOptionComboBox *opt = qstyleoption_cast<QStyleOptionComboBox *>(m_styleoption);
1122         int newWidth = qMax(width, m_contentWidth) + (opt->currentIcon.isNull() ? 0 : opt->iconSize.width());
1123         int newHeight = qMax(height, opt->fontMetrics.height());
1124         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_ComboBox, m_styleoption, QSize(newWidth, newHeight));
1125         break;
1126     }
1127     case Tab: {
1128         QStyleOptionTab *tab = qstyleoption_cast<QStyleOptionTab *>(m_styleoption);
1129 
1130         int contentWidth = tab->fontMetrics.boundingRect(tab->text).width();
1131         int contentHeight = tab->fontMetrics.height();
1132 
1133         if (!tab->icon.isNull()) {
1134             //+4 matches a hardcoded value in QStyle and acts as a margin between the icon and the text.
1135             contentWidth += tab->iconSize.width() + 4;
1136             contentHeight = qMax(contentHeight, tab->iconSize.height());
1137         }
1138 
1139         contentWidth += KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarTabHSpace, tab);
1140         contentHeight += KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarTabVSpace, tab);
1141 
1142         const int newWidth = qMax(width, contentWidth);
1143         const int newHeight = qMax(height, contentHeight);
1144 
1145         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_TabBarTab, m_styleoption, QSize(newWidth, newHeight));
1146 
1147         break;
1148     }
1149     case Slider: {
1150         const auto opt = qstyleoption_cast<const QStyleOptionSlider *>(m_styleoption);
1151         const auto tickPosition = opt->tickPosition;
1152 
1153         // Copied from QSlider
1154         const int SliderLength = 84, TickSpace = 5;
1155         int thick = style()->pixelMetric(QStyle::PM_SliderThickness, m_styleoption, nullptr);
1156         if (tickPosition & QSlider::TicksAbove) {
1157             thick += TickSpace;
1158         }
1159         if (tickPosition & QSlider::TicksBelow) {
1160             thick += TickSpace;
1161         }
1162         int w = thick;
1163         int h = SliderLength;
1164         if (opt->orientation == Qt::Horizontal) {
1165             w = SliderLength;
1166             h = thick;
1167         }
1168         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_Slider, m_styleoption, QSize(w, h));
1169         break;
1170     }
1171     case ProgressBar:
1172         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_ProgressBar, m_styleoption, QSize(width, height));
1173         break;
1174 
1175     case ScrollBar: {
1176         const auto opt = qstyleoption_cast<const QStyleOptionSlider *>(m_styleoption);
1177 
1178         // Copied from QScrollBar
1179         const int scrollBarExtent = KQuickStyleItem::style()->pixelMetric(QStyle::PM_ScrollBarExtent, opt, nullptr);
1180         const int scrollBarSliderMin = KQuickStyleItem::style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, opt, nullptr);
1181         if (opt->orientation == Qt::Horizontal) {
1182             size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent);
1183         } else {
1184             size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin);
1185         }
1186         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_ScrollBar, opt, size);
1187         break;
1188     }
1189     case SpinBox:
1190     case Edit: {
1191         // We have to create a new style option since we might be calling with a QStyleOptionSpinBox
1192         QStyleOptionFrame frame;
1193         //+2 to be consistent with the hardcoded verticalmargin in QLineEdit
1194         int contentHeight = frame.fontMetrics.height() + 2;
1195 
1196         frame.state = m_styleoption->state | QStyle::State_Sunken;
1197         frame.lineWidth = KQuickStyleItem::style()->pixelMetric(QStyle::PM_DefaultFrameWidth, m_styleoption, nullptr);
1198         frame.rect = m_styleoption->rect;
1199         frame.styleObject = this;
1200 
1201         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_LineEdit, &frame, QSize(width, qMax(height, contentHeight)));
1202         if (m_itemType == SpinBox) {
1203             size.setWidth(KQuickStyleItem::style()->sizeFromContents(QStyle::CT_SpinBox, m_styleoption, QSize(width + 2, height)).width());
1204         }
1205         break;
1206     }
1207     case GroupBox: {
1208         QStyleOptionGroupBox *box = qstyleoption_cast<QStyleOptionGroupBox *>(m_styleoption);
1209         QFontMetrics metrics(box->fontMetrics);
1210         int baseWidth = metrics.boundingRect(box->text + QLatin1Char(' ')).width();
1211         int baseHeight = metrics.height() + m_contentHeight;
1212         if (box->subControls & QStyle::SC_GroupBoxCheckBox) {
1213             baseWidth += KQuickStyleItem::style()->pixelMetric(QStyle::PM_IndicatorWidth);
1214             baseWidth += KQuickStyleItem::style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing);
1215             baseHeight = qMax(baseHeight, KQuickStyleItem::style()->pixelMetric(QStyle::PM_IndicatorHeight));
1216         }
1217         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_GroupBox, m_styleoption, QSize(qMax(baseWidth, m_contentWidth), baseHeight));
1218         break;
1219     }
1220     case Header:
1221         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_HeaderSection, m_styleoption, QSize(width, height));
1222         break;
1223     case ItemRow:
1224     case Item: // fall through
1225         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_ItemViewItem, m_styleoption, QSize(width, height));
1226         break;
1227     case MenuBarItem:
1228         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_MenuBarItem, m_styleoption, QSize(width, height));
1229         break;
1230     case MenuBar:
1231         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_MenuBar, m_styleoption, QSize(width, height));
1232         break;
1233     case Menu:
1234         size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_Menu, m_styleoption, QSize(width, height));
1235         break;
1236     case MenuItem:
1237     case ComboBoxItem:
1238         if (static_cast<QStyleOptionMenuItem *>(m_styleoption)->menuItemType == QStyleOptionMenuItem::Scroller) {
1239             size.setHeight(KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuScrollerHeight, nullptr, nullptr));
1240         } else {
1241             size = KQuickStyleItem::style()->sizeFromContents(QStyle::CT_MenuItem, m_styleoption, QSize(width, height));
1242         }
1243         break;
1244     default:
1245         break;
1246     }
1247     return size.expandedTo(QSize(m_contentWidth, m_contentHeight));
1248 }
1249 
1250 qreal KQuickStyleItem::baselineOffset()
1251 {
1252     QRect r;
1253     bool ceilResult = true; // By default baseline offset rounding is done upwards
1254     switch (m_itemType) {
1255     case RadioButton:
1256         r = KQuickStyleItem::style()->subElementRect(QStyle::SE_RadioButtonContents, m_styleoption);
1257         break;
1258     case Button:
1259         r = KQuickStyleItem::style()->subElementRect(QStyle::SE_PushButtonContents, m_styleoption);
1260         break;
1261     case CheckBox:
1262         r = KQuickStyleItem::style()->subElementRect(QStyle::SE_CheckBoxContents, m_styleoption);
1263         break;
1264     case Edit:
1265         r = KQuickStyleItem::style()->subElementRect(QStyle::SE_LineEditContents, m_styleoption);
1266         break;
1267     case ComboBox:
1268         if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(m_styleoption)) {
1269             r = KQuickStyleItem::style()->subControlRect(QStyle::CC_ComboBox, combo, QStyle::SC_ComboBoxEditField);
1270             if (styleName() != QLatin1String("mac")) {
1271                 r.adjust(0, 0, 0, 1);
1272             }
1273         }
1274         break;
1275     case SpinBox:
1276         if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(m_styleoption)) {
1277             r = KQuickStyleItem::style()->subControlRect(QStyle::CC_SpinBox, spinbox, QStyle::SC_SpinBoxEditField);
1278             ceilResult = false;
1279         }
1280         break;
1281     default:
1282         break;
1283     }
1284     if (r.height() > 0) {
1285         const QFontMetrics &fm = m_styleoption->fontMetrics;
1286         int surplus = r.height() - fm.height();
1287         if ((surplus & 1) && ceilResult) {
1288             surplus++;
1289         }
1290         int result = r.top() + surplus / 2 + fm.ascent();
1291         return result;
1292     }
1293 
1294     return 0.;
1295 }
1296 
1297 void KQuickStyleItem::updateBaselineOffset()
1298 {
1299     const qreal baseline = baselineOffset();
1300     if (baseline > 0) {
1301         setBaselineOffset(baseline);
1302     }
1303 }
1304 
1305 void KQuickStyleItem::setContentWidth(int arg)
1306 {
1307     if (m_contentWidth != arg) {
1308         m_contentWidth = arg;
1309         updateSizeHint();
1310         Q_EMIT contentWidthChanged(arg);
1311     }
1312 }
1313 
1314 void KQuickStyleItem::setContentHeight(int arg)
1315 {
1316     if (m_contentHeight != arg) {
1317         m_contentHeight = arg;
1318         updateSizeHint();
1319         updateBaselineOffset();
1320         Q_EMIT contentHeightChanged(arg);
1321     }
1322 }
1323 
1324 void KQuickStyleItem::updateSizeHint()
1325 {
1326     QSize implicitSize = sizeFromContents(m_contentWidth, m_contentHeight);
1327     setImplicitSize(implicitSize.width(), implicitSize.height());
1328 }
1329 
1330 void KQuickStyleItem::updateRect()
1331 {
1332     initStyleOption();
1333     m_styleoption->rect.setWidth(width());
1334     m_styleoption->rect.setHeight(height());
1335 }
1336 
1337 int KQuickStyleItem::pixelMetric(const QString &metric)
1338 {
1339     if (metric == QLatin1String("scrollbarExtent")) {
1340         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_ScrollBarExtent, nullptr);
1341     } else if (metric == QLatin1String("defaultframewidth")) {
1342         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_DefaultFrameWidth, m_styleoption);
1343     } else if (metric == QLatin1String("taboverlap")) {
1344         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr);
1345     } else if (metric == QLatin1String("tabbaseoverlap")) {
1346         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, m_styleoption);
1347     } else if (metric == QLatin1String("tabhspace")) {
1348         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarTabHSpace, nullptr);
1349     } else if (metric == QLatin1String("indicatorwidth")) {
1350         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_IndicatorWidth, nullptr);
1351     } else if (metric == QLatin1String("exclusiveindicatorwidth")) {
1352         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, nullptr);
1353     } else if (metric == QLatin1String("checkboxlabelspacing")) {
1354         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, nullptr);
1355     } else if (metric == QLatin1String("radiobuttonlabelspacing")) {
1356         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_RadioButtonLabelSpacing, nullptr);
1357     } else if (metric == QLatin1String("tabvspace")) {
1358         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarTabVSpace, nullptr);
1359     } else if (metric == QLatin1String("tabbaseheight")) {
1360         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarBaseHeight, nullptr);
1361     } else if (metric == QLatin1String("tabvshift")) {
1362         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_TabBarTabShiftVertical, nullptr);
1363     } else if (metric == QLatin1String("menubarhmargin")) {
1364         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuBarHMargin, nullptr);
1365     } else if (metric == QLatin1String("menubarvmargin")) {
1366         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuBarVMargin, nullptr);
1367     } else if (metric == QLatin1String("menubarpanelwidth")) {
1368         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr);
1369     } else if (metric == QLatin1String("menubaritemspacing")) {
1370         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuBarItemSpacing, nullptr);
1371     } else if (metric == QLatin1String("spacebelowmenubar")) {
1372         return KQuickStyleItem::style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, m_styleoption);
1373     } else if (metric == QLatin1String("menuhmargin")) {
1374         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuHMargin, nullptr);
1375     } else if (metric == QLatin1String("menuvmargin")) {
1376         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuVMargin, nullptr);
1377     } else if (metric == QLatin1String("menupanelwidth")) {
1378         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuPanelWidth, nullptr);
1379     } else if (metric == QLatin1String("submenuoverlap")) {
1380         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_SubMenuOverlap, nullptr);
1381     } else if (metric == QLatin1String("splitterwidth")) {
1382         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_SplitterWidth, nullptr);
1383     } else if (metric == QLatin1String("scrollbarspacing")) {
1384         return abs(KQuickStyleItem::style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, nullptr));
1385     } else if (metric == QLatin1String("treeviewindentation")) {
1386         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_TreeViewIndentation, nullptr);
1387     } else if (metric == QLatin1String("layouthorizontalspacing")) {
1388         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, nullptr);
1389     } else if (metric == QLatin1String("layoutverticalspacing")) {
1390         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing, nullptr);
1391     } else if (metric == QLatin1String("layoutleftmargin")) {
1392         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_LayoutLeftMargin, nullptr);
1393     } else if (metric == QLatin1String("layouttopmargin")) {
1394         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_LayoutTopMargin, nullptr);
1395     } else if (metric == QLatin1String("layoutrightmargin")) {
1396         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_LayoutRightMargin, nullptr);
1397     } else if (metric == QLatin1String("layoutbottommargin")) {
1398         return KQuickStyleItem::style()->pixelMetric(QStyle::PM_LayoutBottomMargin, nullptr);
1399     }
1400     return 0;
1401 }
1402 
1403 QVariant KQuickStyleItem::styleHint(const QString &metric)
1404 {
1405     initStyleOption();
1406     if (metric == QLatin1String("comboboxpopup")) {
1407         return KQuickStyleItem::style()->styleHint(QStyle::SH_ComboBox_Popup, m_styleoption);
1408     } else if (metric == QLatin1String("highlightedTextColor")) {
1409         return m_styleoption->palette.highlightedText().color().name();
1410     } else if (metric == QLatin1String("textColor")) {
1411         QPalette pal = m_styleoption->palette;
1412         pal.setCurrentColorGroup(active() ? QPalette::Active : QPalette::Inactive);
1413         return pal.text().color().name();
1414     } else if (metric == QLatin1String("focuswidget")) {
1415         return KQuickStyleItem::style()->styleHint(QStyle::SH_FocusFrame_AboveWidget);
1416     } else if (metric == QLatin1String("tabbaralignment")) {
1417         int result = KQuickStyleItem::style()->styleHint(QStyle::SH_TabBar_Alignment);
1418         if (result == Qt::AlignCenter) {
1419             return QStringLiteral("center");
1420         }
1421         return QStringLiteral("left");
1422     } else if (metric == QLatin1String("externalScrollBars")) {
1423         return KQuickStyleItem::style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents);
1424     } else if (metric == QLatin1String("scrollToClickPosition")) {
1425         return KQuickStyleItem::style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition);
1426     } else if (metric == QLatin1String("activateItemOnSingleClick")) {
1427         return KQuickStyleItem::style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick);
1428     } else if (metric == QLatin1String("submenupopupdelay")) {
1429         return KQuickStyleItem::style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, m_styleoption);
1430     } else if (metric == QLatin1String("wheelScrollLines")) {
1431         return qApp->wheelScrollLines();
1432     }
1433     return 0;
1434 
1435     // Add SH_Menu_SpaceActivatesItem
1436 }
1437 
1438 void KQuickStyleItem::setHints(const QVariantMap &hints)
1439 {
1440     if (m_hints != hints) {
1441         m_hints = hints;
1442         initStyleOption();
1443         updateSizeHint();
1444         polish();
1445         if (m_styleoption->state & QStyle::State_Mini) {
1446             m_font.setPointSize(9.);
1447             Q_EMIT fontChanged();
1448         } else if (m_styleoption->state & QStyle::State_Small) {
1449             m_font.setPointSize(11.);
1450             Q_EMIT fontChanged();
1451         } else {
1452             Q_EMIT hintChanged();
1453         }
1454     }
1455 }
1456 
1457 void KQuickStyleItem::resetHints()
1458 {
1459     m_hints.clear();
1460 }
1461 
1462 void KQuickStyleItem::setElementType(const QString &str)
1463 {
1464     if (m_type == str) {
1465         return;
1466     }
1467 
1468     m_type = str;
1469 
1470     Q_EMIT elementTypeChanged();
1471     if (m_styleoption) {
1472         delete m_styleoption;
1473         m_styleoption = nullptr;
1474     }
1475 
1476     // Only enable visible if the widget can animate
1477     if (str == QLatin1String("menu")) {
1478         m_itemType = Menu;
1479     } else if (str == QLatin1String("menuitem")) {
1480         m_itemType = MenuItem;
1481     } else if (str == QLatin1String("item") || str == QLatin1String("itemrow") || str == QLatin1String("header")) {
1482         if (str == QLatin1String("header")) {
1483             m_itemType = Header;
1484         } else {
1485             m_itemType = str == QLatin1String("item") ? Item : ItemRow;
1486         }
1487     } else if (str == QLatin1String("itembranchindicator")) {
1488         m_itemType = ItemBranchIndicator;
1489     } else if (str == QLatin1String("groupbox")) {
1490         m_itemType = GroupBox;
1491     } else if (str == QLatin1String("tab")) {
1492         m_itemType = Tab;
1493     } else if (str == QLatin1String("tabframe")) {
1494         m_itemType = TabFrame;
1495     } else if (str == QLatin1String("comboboxitem")) {
1496         // Gtk uses qobject cast, hence we need to separate this from menuitem
1497         // On macOS, we temporarily use the menu item because it has more accurate
1498         // palette.
1499         m_itemType = ComboBoxItem;
1500     } else if (str == QLatin1String("toolbar")) {
1501         m_itemType = ToolBar;
1502     } else if (str == QLatin1String("toolbutton")) {
1503         m_itemType = ToolButton;
1504     } else if (str == QLatin1String("slider")) {
1505         m_itemType = Slider;
1506     } else if (str == QLatin1String("frame")) {
1507         m_itemType = Frame;
1508     } else if (str == QLatin1String("combobox")) {
1509         m_itemType = ComboBox;
1510     } else if (str == QLatin1String("splitter")) {
1511         m_itemType = Splitter;
1512     } else if (str == QLatin1String("progressbar")) {
1513         m_itemType = ProgressBar;
1514     } else if (str == QLatin1String("button")) {
1515         m_itemType = Button;
1516     } else if (str == QLatin1String("checkbox")) {
1517         m_itemType = CheckBox;
1518     } else if (str == QLatin1String("radiobutton")) {
1519         m_itemType = RadioButton;
1520     } else if (str == QLatin1String("edit")) {
1521         m_itemType = Edit;
1522     } else if (str == QLatin1String("spinbox")) {
1523         m_itemType = SpinBox;
1524     } else if (str == QLatin1String("scrollbar")) {
1525         m_itemType = ScrollBar;
1526     } else if (str == QLatin1String("widget")) {
1527         m_itemType = Widget;
1528     } else if (str == QLatin1String("focusframe")) {
1529         m_itemType = FocusFrame;
1530     } else if (str == QLatin1String("focusrect")) {
1531         m_itemType = FocusRect;
1532     } else if (str == QLatin1String("dial")) {
1533         m_itemType = Dial;
1534     } else if (str == QLatin1String("statusbar")) {
1535         m_itemType = StatusBar;
1536     } else if (str == QLatin1String("machelpbutton")) {
1537         m_itemType = MacHelpButton;
1538     } else if (str == QLatin1String("scrollareacorner")) {
1539         m_itemType = ScrollAreaCorner;
1540     } else if (str == QLatin1String("menubar")) {
1541         m_itemType = MenuBar;
1542     } else if (str == QLatin1String("menubaritem")) {
1543         m_itemType = MenuBarItem;
1544     } else if (str == QLatin1String("delaybutton")) {
1545         m_itemType = DelayButton;
1546     } else {
1547         m_itemType = Undefined;
1548     }
1549     Q_EMIT leftPaddingChanged();
1550     Q_EMIT rightPaddingChanged();
1551     Q_EMIT topPaddingChanged();
1552     Q_EMIT bottomPaddingChanged();
1553     updateSizeHint();
1554     polish();
1555 }
1556 
1557 QRectF KQuickStyleItem::subControlRect(const QString &subcontrolString)
1558 {
1559     QStyle::SubControl subcontrol = QStyle::SC_None;
1560     initStyleOption();
1561     switch (m_itemType) {
1562     case SpinBox: {
1563         QStyle::ComplexControl control = QStyle::CC_SpinBox;
1564         if (subcontrolString == QLatin1String("down")) {
1565             subcontrol = QStyle::SC_SpinBoxDown;
1566         } else if (subcontrolString == QLatin1String("up")) {
1567             subcontrol = QStyle::SC_SpinBoxUp;
1568         } else if (subcontrolString == QLatin1String("edit")) {
1569             subcontrol = QStyle::SC_SpinBoxEditField;
1570         }
1571         return KQuickStyleItem::style()->subControlRect(control, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), subcontrol);
1572 
1573         break;
1574     }
1575     case Slider: {
1576         QStyle::ComplexControl control = QStyle::CC_Slider;
1577         if (subcontrolString == QLatin1String("handle")) {
1578             subcontrol = QStyle::SC_SliderHandle;
1579         } else if (subcontrolString == QLatin1String("groove")) {
1580             subcontrol = QStyle::SC_SliderGroove;
1581         }
1582         return KQuickStyleItem::style()->subControlRect(control, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), subcontrol);
1583 
1584         break;
1585     }
1586     case ScrollBar: {
1587         QStyle::ComplexControl control = QStyle::CC_ScrollBar;
1588         if (subcontrolString == QLatin1String("slider")) {
1589             subcontrol = QStyle::SC_ScrollBarSlider;
1590         } else if (subcontrolString == QLatin1String("groove")) {
1591             subcontrol = QStyle::SC_ScrollBarGroove;
1592         } else if (subcontrolString == QLatin1String("handle")) {
1593             subcontrol = QStyle::SC_ScrollBarSlider;
1594         } else if (subcontrolString == QLatin1String("add")) {
1595             subcontrol = QStyle::SC_ScrollBarAddPage;
1596         } else if (subcontrolString == QLatin1String("sub")) {
1597             subcontrol = QStyle::SC_ScrollBarSubPage;
1598         }
1599         return KQuickStyleItem::style()->subControlRect(control, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), subcontrol);
1600         break;
1601     }
1602     case ItemBranchIndicator: {
1603         QStyleOption opt;
1604         opt.rect = QRect(0, 0, implicitWidth(), implicitHeight());
1605         return KQuickStyleItem::style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, nullptr);
1606     }
1607     default:
1608         break;
1609     }
1610     return QRectF();
1611 }
1612 
1613 void KQuickStyleItem::paint(QPainter *painter)
1614 {
1615     initStyleOption();
1616     if (QStyleOptionMenuItem *opt = qstyleoption_cast<QStyleOptionMenuItem *>(m_styleoption)) {
1617         painter->setFont(opt->font);
1618     } else {
1619         QFont font;
1620         if (m_styleoption->state & QStyle::State_Mini) {
1621             font = qApp->font("QMiniFont");
1622         } else if (m_styleoption->state & QStyle::State_Small) {
1623             font = qApp->font("QSmallFont");
1624         } else {
1625             font = QApplication::font(classNameForItem());
1626         }
1627         painter->setFont(font);
1628     }
1629 
1630     switch (m_itemType) {
1631     case Button:
1632         KQuickStyleItem::style()->drawControl(QStyle::CE_PushButton, m_styleoption, painter);
1633         break;
1634     case ItemRow: {
1635         QPixmap pixmap;
1636         // Only draw through style once
1637         const QString pmKey = QLatin1String("itemrow") % QString::number(m_styleoption->state, 16) % activeControl();
1638         if (!QPixmapCache::find(pmKey, &pixmap) || pixmap.width() < width() || height() != pixmap.height()) {
1639             int newSize = width();
1640             pixmap = QPixmap(newSize, height());
1641             pixmap.fill(Qt::transparent);
1642             QPainter pixpainter(&pixmap);
1643             KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelItemViewRow, m_styleoption, &pixpainter);
1644             if ((styleName() == QLatin1String("mac") || !KQuickStyleItem::style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected)) && selected()) {
1645                 QPalette pal = QApplication::palette("QAbstractItemView");
1646                 pal.setCurrentColorGroup(m_styleoption->palette.currentColorGroup());
1647                 pixpainter.fillRect(m_styleoption->rect, pal.highlight());
1648             }
1649             QPixmapCache::insert(pmKey, pixmap);
1650         }
1651         painter->drawPixmap(0, 0, pixmap);
1652         break;
1653     }
1654     case Item:
1655         KQuickStyleItem::style()->drawControl(QStyle::CE_ItemViewItem, m_styleoption, painter);
1656         break;
1657     case ItemBranchIndicator:
1658         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_IndicatorBranch, m_styleoption, painter);
1659         break;
1660     case Header:
1661         KQuickStyleItem::style()->drawControl(QStyle::CE_Header, m_styleoption, painter);
1662         break;
1663     case ToolButton:
1664         KQuickStyleItem::style()->drawComplexControl(QStyle::CC_ToolButton, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), painter);
1665         break;
1666     case Tab: {
1667         if (!isKeyFocusReason(m_focusReason)) {
1668             m_styleoption->state &= ~QStyle::State_HasFocus;
1669         }
1670         KQuickStyleItem::style()->drawControl(QStyle::CE_TabBarTab, m_styleoption, painter);
1671         break;
1672     }
1673     case Frame:
1674         m_styleoption->state |= QStyle::State_Sunken;
1675         m_styleoption->state &= ~QStyle::State_Raised;
1676         KQuickStyleItem::style()->drawControl(QStyle::CE_ShapedFrame, m_styleoption, painter);
1677         break;
1678     case FocusFrame:
1679         KQuickStyleItem::style()->drawControl(QStyle::CE_FocusFrame, m_styleoption, painter);
1680         break;
1681     case FocusRect:
1682         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_FrameFocusRect, m_styleoption, painter);
1683         break;
1684     case TabFrame:
1685         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_FrameTabWidget, m_styleoption, painter);
1686         break;
1687     case MenuBar:
1688         KQuickStyleItem::style()->drawControl(QStyle::CE_MenuBarEmptyArea, m_styleoption, painter);
1689         break;
1690     case MenuBarItem:
1691         KQuickStyleItem::style()->drawControl(QStyle::CE_MenuBarItem, m_styleoption, painter);
1692         break;
1693     case MenuItem:
1694     case ComboBoxItem: { // fall through
1695         QStyle::ControlElement menuElement =
1696             static_cast<QStyleOptionMenuItem *>(m_styleoption)->menuItemType == QStyleOptionMenuItem::Scroller ? QStyle::CE_MenuScroller : QStyle::CE_MenuItem;
1697         KQuickStyleItem::style()->drawControl(menuElement, m_styleoption, painter);
1698         break;
1699     }
1700     case CheckBox:
1701         KQuickStyleItem::style()->drawControl(QStyle::CE_CheckBox, m_styleoption, painter);
1702         break;
1703     case RadioButton:
1704         KQuickStyleItem::style()->drawControl(QStyle::CE_RadioButton, m_styleoption, painter);
1705         break;
1706     case Edit:
1707         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelLineEdit, m_styleoption, painter);
1708         break;
1709     case MacHelpButton:
1710         // Not managed as mac is not supported
1711         break;
1712     case Widget:
1713         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_Widget, m_styleoption, painter);
1714         break;
1715     case ScrollAreaCorner:
1716         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, m_styleoption, painter);
1717         break;
1718     case Splitter:
1719         if (m_styleoption->rect.width() == 1) {
1720             painter->fillRect(0, 0, width(), height(), m_styleoption->palette.dark().color());
1721         } else {
1722             KQuickStyleItem::style()->drawControl(QStyle::CE_Splitter, m_styleoption, painter);
1723         }
1724         break;
1725     case ComboBox: {
1726         KQuickStyleItem::style()->drawComplexControl(QStyle::CC_ComboBox, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), painter);
1727         // This is needed on macOS as it will use the painter color and ignore the palette
1728         QPen pen = painter->pen();
1729         painter->setPen(m_styleoption->palette.text().color());
1730         KQuickStyleItem::style()->drawControl(QStyle::CE_ComboBoxLabel, m_styleoption, painter);
1731         painter->setPen(pen);
1732         break;
1733     }
1734     case SpinBox:
1735 #ifdef Q_OS_MAC
1736         // macstyle depends on the embedded qlineedit to fill the editfield background
1737         if (styleName() == QLatin1String("mac")) {
1738             QRect editRect = KQuickStyleItem::style()->subControlRect(QStyle::CC_SpinBox,
1739                                                                       qstyleoption_cast<QStyleOptionComplex *>(m_styleoption),
1740                                                                       QStyle::SC_SpinBoxEditField);
1741             painter->fillRect(editRect.adjusted(-1, -1, 1, 1), m_styleoption->palette.base());
1742         }
1743 #endif
1744         KQuickStyleItem::style()->drawComplexControl(QStyle::CC_SpinBox, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), painter);
1745         break;
1746     case Slider:
1747         KQuickStyleItem::style()->drawComplexControl(QStyle::CC_Slider, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), painter);
1748         break;
1749     case Dial:
1750         KQuickStyleItem::style()->drawComplexControl(QStyle::CC_Dial, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), painter);
1751         break;
1752     case ProgressBar:
1753         KQuickStyleItem::style()->drawControl(QStyle::CE_ProgressBar, m_styleoption, painter);
1754         break;
1755     case ToolBar:
1756         painter->fillRect(m_styleoption->rect, m_styleoption->palette.window().color());
1757         KQuickStyleItem::style()->drawControl(QStyle::CE_ToolBar, m_styleoption, painter);
1758         painter->save();
1759         painter->setPen(styleName() != QLatin1String("fusion") ? m_styleoption->palette.dark().color().darker(120)
1760                                                                : m_styleoption->palette.window().color().lighter(107));
1761         painter->drawLine(m_styleoption->rect.bottomLeft(), m_styleoption->rect.bottomRight());
1762         painter->restore();
1763         break;
1764     case StatusBar: {
1765         painter->fillRect(m_styleoption->rect, m_styleoption->palette.window().color());
1766         painter->setPen(m_styleoption->palette.dark().color().darker(120));
1767         painter->drawLine(m_styleoption->rect.topLeft(), m_styleoption->rect.topRight());
1768         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelStatusBar, m_styleoption, painter);
1769         break;
1770     }
1771     case GroupBox:
1772         KQuickStyleItem::style()->drawComplexControl(QStyle::CC_GroupBox, qstyleoption_cast<QStyleOptionComplex *>(m_styleoption), painter);
1773         break;
1774     case ScrollBar:
1775         KQuickStyleItem::style()->drawComplexControl(QStyle::CC_ScrollBar, qstyleoption_cast<QStyleOptionSlider *>(m_styleoption), painter);
1776         break;
1777     case Menu: {
1778         QStyleHintReturnMask val;
1779         KQuickStyleItem::style()->styleHint(QStyle::SH_Menu_Mask, m_styleoption, nullptr, &val);
1780         painter->save();
1781         painter->setClipRegion(val.region);
1782         painter->fillRect(m_styleoption->rect, m_styleoption->palette.window());
1783         painter->restore();
1784         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelMenu, m_styleoption, painter);
1785 
1786         if (int fw = KQuickStyleItem::style()->pixelMetric(QStyle::PM_MenuPanelWidth)) {
1787             QStyleOptionFrame frame;
1788             frame.state = QStyle::State_None;
1789             frame.lineWidth = fw;
1790             frame.midLineWidth = 0;
1791             frame.rect = m_styleoption->rect;
1792             frame.styleObject = this;
1793             frame.palette = m_styleoption->palette;
1794             KQuickStyleItem::style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, painter);
1795         }
1796         break;
1797     }
1798     case DelayButton: { // Adapted from Spectacle's ProgressButton made by David Redondo.
1799         // Draw Button without text and icon, note the missing text and icon in options
1800         QStyleOption baseStyleOptions = *m_styleoption;
1801         baseStyleOptions.state.setFlag(QStyle::State_Enabled, !baseStyleOptions.state.testFlag(QStyle::State_Sunken));
1802         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelButtonTool, &baseStyleOptions, painter);
1803         qreal progress = qreal(value()) / qreal(maximum());
1804         if (!qFuzzyIsNull(progress)) {
1805             // Draw overlay
1806             QStyleOption overlayOption;
1807             overlayOption.palette = m_styleoption->palette;
1808             overlayOption.palette.setBrush(QPalette::Button, m_styleoption->palette.highlight());
1809             overlayOption.rect = m_styleoption->direction == Qt::LeftToRight ?
1810                 QRect(0, 0, m_styleoption->rect.width() * progress, m_styleoption->rect.height())
1811                 : QRect(QPoint(m_styleoption->rect.width() * (1 - progress), 0), m_styleoption->rect.size());
1812             overlayOption.state.setFlag(QStyle::State_Sunken, m_styleoption->state.testFlag(QStyle::State_Sunken));
1813             painter->setCompositionMode(QPainter::CompositionMode_SourceAtop);
1814             painter->setOpacity(0.5);
1815             KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelButtonTool, &overlayOption, painter);
1816         }
1817         // Finally draw text and icon and outline
1818         painter->setOpacity(1);
1819         KQuickStyleItem::style()->drawControl(QStyle::CE_ToolButtonLabel, m_styleoption, painter);
1820         break;
1821     }
1822     default:
1823         break;
1824     }
1825 }
1826 
1827 qreal KQuickStyleItem::textWidth(const QString &text)
1828 {
1829     QFontMetricsF fm = QFontMetricsF(m_styleoption->fontMetrics);
1830     return fm.boundingRect(text).width();
1831 }
1832 
1833 qreal KQuickStyleItem::textHeight(const QString &text)
1834 {
1835     QFontMetricsF fm = QFontMetricsF(m_styleoption->fontMetrics);
1836     return text.isEmpty() ? fm.height() : fm.boundingRect(text).height();
1837 }
1838 
1839 QString KQuickStyleItem::elidedText(const QString &text, int elideMode, int width)
1840 {
1841     return m_styleoption->fontMetrics.elidedText(text, Qt::TextElideMode(elideMode), width);
1842 }
1843 
1844 bool KQuickStyleItem::hasThemeIcon(const QString &icon) const
1845 {
1846     return QIcon::hasThemeIcon(icon);
1847 }
1848 
1849 void KQuickStyleItem::updateFocusReason()
1850 {
1851     if (m_control) {
1852         auto reason = m_focusReasonProperty.read(m_control).value<Qt::FocusReason>();
1853         if (m_focusReason != reason) {
1854             m_focusReason = reason;
1855             polish();
1856         }
1857     }
1858 }
1859 
1860 bool KQuickStyleItem::event(QEvent *ev)
1861 {
1862     if (ev->type() == QEvent::StyleAnimationUpdate) {
1863         if (isVisible()) {
1864             ev->accept();
1865             polish();
1866         }
1867         return true;
1868     } else if (ev->type() == Kirigami::Platform::TabletModeChangedEvent::type) {
1869         Q_EMIT leftPaddingChanged();
1870         Q_EMIT rightPaddingChanged();
1871         Q_EMIT topPaddingChanged();
1872         Q_EMIT bottomPaddingChanged();
1873         updateSizeHint();
1874         polish();
1875         return true;
1876     }
1877 
1878     return QQuickItem::event(ev);
1879 }
1880 
1881 void KQuickStyleItem::setTextureWidth(int w)
1882 {
1883     if (m_textureWidth == w) {
1884         return;
1885     }
1886     m_textureWidth = w;
1887     Q_EMIT textureWidthChanged(m_textureWidth);
1888     update();
1889 }
1890 
1891 void KQuickStyleItem::setTextureHeight(int h)
1892 {
1893     if (m_textureHeight == h) {
1894         return;
1895     }
1896     m_textureHeight = h;
1897     Q_EMIT textureHeightChanged(m_textureHeight);
1898     update();
1899 }
1900 
1901 QQuickItem *KQuickStyleItem::control() const
1902 {
1903     return m_control;
1904 }
1905 
1906 void KQuickStyleItem::setControl(QQuickItem *control)
1907 {
1908     if (control == m_control) {
1909         return;
1910     }
1911 
1912     if (m_control) {
1913         m_control->removeEventFilter(this);
1914         disconnect(m_control, nullptr, this, nullptr);
1915     }
1916 
1917     m_control = control;
1918     m_focusReasonProperty = {};
1919     m_focusReason = Qt::NoFocusReason;
1920 
1921     if (m_control) {
1922         // focusReasonChanged is a signal of QQuickControl class which is not exposed.
1923         const auto mo = m_control->metaObject();
1924         m_focusReasonProperty = mo->property(mo->indexOfProperty("focusReason"));
1925         if (m_focusReasonProperty.isValid()) {
1926             // wish there was something like QMetaMethod::fromSignal but for slots
1927             const auto slot = metaObject()->method(metaObject()->indexOfSlot("updateFocusReason()"));
1928             // also wish there was a way to connect QMetaMethod to arbitrary member function
1929             connect(m_control, m_focusReasonProperty.notifySignal(), this, slot);
1930             updateFocusReason();
1931         }
1932 
1933         m_control->installEventFilter(this);
1934 
1935         if (m_control->window()) {
1936             m_window = m_control->window();
1937             m_window->installEventFilter(this);
1938         }
1939         connect(m_control, &QQuickItem::windowChanged, this, [this](QQuickWindow *window) {
1940             if (m_window) {
1941                 m_window->removeEventFilter(this);
1942             }
1943             m_window = window;
1944             if (m_window) {
1945                 m_window->installEventFilter(this);
1946             }
1947         });
1948     }
1949 
1950     Q_EMIT controlChanged();
1951 }
1952 
1953 QSGNode *KQuickStyleItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1954 {
1955     if (m_image.isNull()) {
1956         delete node;
1957         return nullptr;
1958     }
1959 
1960     QSGNinePatchNode *styleNode = static_cast<QSGNinePatchNode *>(node);
1961     if (!styleNode) {
1962         styleNode = window()->createNinePatchNode();
1963     }
1964 
1965 #ifdef QSG_RUNTIME_DESCRIPTION
1966     qsgnode_set_description(styleNode, QString::fromLatin1("%1:%2, '%3'").arg(styleName(), elementType(), text()));
1967 #endif
1968 
1969     styleNode->setTexture(window()->createTextureFromImage(m_image, QQuickWindow::TextureCanUseAtlas));
1970 
1971     auto scale = window()->effectiveDevicePixelRatio();
1972     QRectF bounds = QRectF(boundingRect().topLeft(), QSizeF{m_image.width() / scale, m_image.height() / scale});
1973 
1974     QPointF globalPixelPos = mapToScene(bounds.topLeft()) * scale;
1975     QPointF posAdjust = QPointF(globalPixelPos.x() - std::round(globalPixelPos.x()), globalPixelPos.y() - std::round(globalPixelPos.y()))
1976         / scale;
1977     bounds.moveTopLeft(bounds.topLeft() - posAdjust);
1978 
1979     styleNode->setBounds(bounds);
1980     styleNode->setDevicePixelRatio(scale);
1981     styleNode->setPadding(m_border.left(), m_border.top(), m_border.right(), m_border.bottom());
1982     styleNode->update();
1983 
1984     return styleNode;
1985 }
1986 
1987 void KQuickStyleItem::updatePolish()
1988 {
1989     if (isVisible() && width() >= 1 && height() >= 1) { // Note these are reals so 1 pixel is minimum
1990         const qreal devicePixelRatio = window() ? window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio();
1991         const QSize size = QSize(m_textureWidth > 0 ? m_textureWidth : width(), m_textureHeight > 0 ? m_textureHeight : height()) * devicePixelRatio;
1992 
1993         if (m_image.size() != size) {
1994             m_image = QImage(size, QImage::Format_ARGB32_Premultiplied);
1995         }
1996         m_image.setDevicePixelRatio(devicePixelRatio);
1997         m_image.fill(Qt::transparent);
1998         QPainter painter(&m_image);
1999         painter.setLayoutDirection(qApp->layoutDirection());
2000         paint(&painter);
2001         QQuickItem::update();
2002     } else if (!m_image.isNull()) {
2003         m_image = QImage();
2004         QQuickItem::update();
2005     }
2006 }
2007 
2008 bool KQuickStyleItem::eventFilter(QObject *watched, QEvent *event)
2009 {
2010     if (watched == m_control) {
2011         // Page accepts mouse events without doing anything with them (for a workaround wrt dragging from empty areas of flickables) when the interaction is
2012         // pure mouse, steal events from them, so a parent handler can initiate a window drag from empty areas, either Kirigami.ApplicationWindow or the breeze
2013         // style from a QQwuickwidget
2014         if (event->type() == QEvent::MouseButtonPress) {
2015             QMouseEvent *me = static_cast<QMouseEvent *>(event);
2016             if (me->source() == Qt::MouseEventNotSynthesized && watched->inherits("QQuickPage")) {
2017                 event->setAccepted(false);
2018                 return true;
2019             }
2020         }
2021     } else if (watched == m_window.data()) {
2022         if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
2023             QKeyEvent *ke = static_cast<QKeyEvent *>(event);
2024             if (ke->key() == Qt::Key_Alt) {
2025                 polish();
2026             }
2027         }
2028     } else if (watched == qGuiApp) {
2029         if (event->type() == QEvent::ApplicationFontChange) {
2030             QMetaObject::invokeMethod(this, &KQuickStyleItem::updateSizeHint, Qt::QueuedConnection);
2031         }
2032     }
2033 
2034     return QQuickItem::eventFilter(watched, event);
2035 }
2036 
2037 void KQuickStyleItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
2038 {
2039     if (change == QQuickItem::ItemVisibleHasChanged || change == QQuickItem::ItemEnabledHasChanged || change == QQuickItem::ItemDevicePixelRatioHasChanged) {
2040         polish();
2041     }
2042 
2043     QQuickItem::itemChange(change, value);
2044 }
2045 
2046 void KQuickStyleItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
2047 {
2048     QQuickItem::geometryChange(newGeometry, oldGeometry);
2049 
2050     if (!newGeometry.isEmpty() && newGeometry != oldGeometry) {
2051         polish();
2052         updateRect();
2053 
2054         if (newGeometry.height() != oldGeometry.height()) {
2055             updateBaselineOffset();
2056         }
2057     }
2058 }
2059 
2060 void KQuickStyleItem::styleChanged()
2061 {
2062     // It should be safe to use qApp->style() unguarded here, because the signal
2063     // will only have been connected if qApp is a QApplication.
2064     Q_ASSERT(qobject_cast<QApplication *>(QCoreApplication::instance()));
2065     auto *style = qApp->style();
2066     if (!style || QCoreApplication::closingDown()) {
2067         return;
2068     }
2069 
2070     Q_ASSERT(style != sender());
2071 
2072     connect(style, &QObject::destroyed, this, &KQuickStyleItem::styleChanged);
2073 
2074     updateSizeHint();
2075     polish();
2076     Q_EMIT styleNameChanged();
2077 }
2078 
2079 QPixmap QQuickTableRowImageProvider1::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
2080 {
2081     Q_UNUSED(requestedSize);
2082     int width = 16;
2083     int height = 16;
2084     if (size) {
2085         *size = QSize(width, height);
2086     }
2087 
2088     QPixmap pixmap(width, height);
2089 
2090     QStyleOptionViewItem opt;
2091     opt.state |= QStyle::State_Enabled;
2092     opt.rect = QRect(0, 0, width, height);
2093     QString style = QString::fromLatin1(KQuickStyleItem::style()->metaObject()->className());
2094     opt.features = {};
2095 
2096     if (id.contains(QLatin1String("selected"))) {
2097         opt.state |= QStyle::State_Selected;
2098     }
2099 
2100     if (id.contains(QLatin1String("active"))) {
2101         opt.state |= QStyle::State_Active;
2102         opt.palette.setCurrentColorGroup(QPalette::Active);
2103     } else {
2104         opt.palette.setCurrentColorGroup(QPalette::Inactive);
2105     }
2106 
2107     if (id.contains(QLatin1String("alternate"))) {
2108         opt.features |= QStyleOptionViewItem::Alternate;
2109     }
2110 
2111     QPalette pal = QApplication::palette("QAbstractItemView");
2112     if (opt.state & QStyle::State_Selected
2113         && (style.contains(QLatin1String("Mac")) || !KQuickStyleItem::style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected))) {
2114         pal.setCurrentColorGroup(opt.palette.currentColorGroup());
2115         pixmap.fill(pal.highlight().color());
2116     } else {
2117         pixmap.fill(pal.base().color());
2118         QPainter pixpainter(&pixmap);
2119         KQuickStyleItem::style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, &pixpainter);
2120     }
2121     return pixmap;
2122 }
2123 
2124 #include "moc_kquickpadding_p.cpp"
2125 #include "moc_kquickstyleitem_p.cpp"