File indexing completed on 2024-09-29 09:34:03
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"