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