File indexing completed on 2024-04-14 14:31:02

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