File indexing completed on 2024-04-28 05:26:26
0001 /* SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0002 * SPDX-FileCopyrightText: 2016 The Qt Company Ltd. 0003 * SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com> 0004 * SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk> 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "breezestyle.h" 0009 0010 #include "breezeanimations.h" 0011 #include "breezeblurhelper.h" 0012 #include "breezeframeshadow.h" 0013 #include "breezemdiwindowshadow.h" 0014 #include "breezemetrics.h" 0015 #include "breezemnemonics.h" 0016 #include "breezepropertynames.h" 0017 #include "breezeshadowhelper.h" 0018 #include "breezesplitterproxy.h" 0019 #include "breezestyleconfigdata.h" 0020 #include "breezetoolsareamanager.h" 0021 #include "breezewidgetexplorer.h" 0022 #include "breezewindowmanager.h" 0023 0024 #include <KColorUtils> 0025 #include <KIconLoader> 0026 0027 #include <QApplication> 0028 #include <QBitmap> 0029 #include <QCheckBox> 0030 #include <QComboBox> 0031 #include <QDBusConnection> 0032 #include <QDial> 0033 #include <QDialog> 0034 #include <QDialogButtonBox> 0035 #include <QFormLayout> 0036 #include <QGraphicsItem> 0037 #include <QGraphicsProxyWidget> 0038 #include <QGraphicsView> 0039 #include <QGroupBox> 0040 #include <QItemDelegate> 0041 #include <QLineEdit> 0042 #include <QMainWindow> 0043 #include <QMdiArea> 0044 #include <QMenu> 0045 #include <QMenuBar> 0046 #include <QMetaEnum> 0047 #include <QPainter> 0048 #include <QPushButton> 0049 #include <QRadioButton> 0050 #include <QScrollBar> 0051 #include <QSplitterHandle> 0052 #include <QStackedLayout> 0053 #include <QTextEdit> 0054 #include <QToolBar> 0055 #include <QToolBox> 0056 #include <QToolButton> 0057 #include <QTreeView> 0058 #include <QWidgetAction> 0059 0060 #if BREEZE_HAVE_QTQUICK 0061 #include <KCoreAddons> 0062 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0063 #include <Kirigami/Platform/TabletModeWatcher> 0064 using TabletModeWatcher = Kirigami::Platform::TabletModeWatcher; 0065 #else 0066 #if __has_include(<Kirigami/TabletModeWatcher>) 0067 // the namespaced include is new in KF 5.91 0068 #include <Kirigami/TabletModeWatcher> 0069 #else 0070 #include <TabletModeWatcher> 0071 #endif 0072 using TabletModeWatcher = Kirigami::TabletModeWatcher; 0073 #endif 0074 #include <QQuickWindow> 0075 #endif 0076 0077 namespace BreezePrivate 0078 { 0079 // needed to keep track of tabbars when being dragged 0080 class TabBarData : public QObject 0081 { 0082 public: 0083 //* constructor 0084 explicit TabBarData(QObject *parent) 0085 : QObject(parent) 0086 { 0087 } 0088 0089 //* assign target tabBar 0090 void lock(const QWidget *widget) 0091 { 0092 _tabBar = widget; 0093 } 0094 0095 //* true if tabbar is locked 0096 bool isLocked(const QWidget *widget) const 0097 { 0098 return _tabBar && _tabBar.data() == widget; 0099 } 0100 0101 //* release 0102 void release() 0103 { 0104 _tabBar.clear(); 0105 } 0106 0107 private: 0108 //* pointer to target tabBar 0109 Breeze::WeakPointer<const QWidget> _tabBar; 0110 }; 0111 0112 //* needed to have spacing added to items in combobox 0113 class ComboBoxItemDelegate : public QItemDelegate 0114 { 0115 public: 0116 //* constructor 0117 explicit ComboBoxItemDelegate(QAbstractItemView *parent) 0118 : QItemDelegate(parent) 0119 , _proxy(parent->itemDelegate()) 0120 , _itemMargin(Breeze::Metrics::ItemView_ItemMarginWidth) 0121 { 0122 } 0123 0124 //* paint 0125 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override 0126 { 0127 painter->setRenderHints(QPainter::Antialiasing); 0128 // if the app sets an item delegate that isn't the default, use its drawing... 0129 if (_proxy && _proxy->metaObject()->className() != QStringLiteral("QComboBoxDelegate")) { 0130 _proxy.data()->paint(painter, option, index); 0131 return; 0132 } 0133 0134 // otherwise we draw the selected/highlighted background ourself... 0135 if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) { 0136 using namespace Breeze; 0137 0138 auto c = option.palette.brush((option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled, QPalette::Highlight).color(); 0139 0140 painter->setPen(c); 0141 c.setAlphaF(c.alphaF() * 0.3); 0142 painter->setBrush(c); 0143 auto radius = Metrics::Frame_FrameRadius - (0.5 * PenWidth::Frame); 0144 painter->drawRoundedRect(QRectF(option.rect).adjusted(0.5, 0.5, -0.5, -0.5), radius, radius); 0145 } 0146 0147 // and ask the base class to do everything else for us besides the selected/highlighted part which we just did 0148 auto opt = option; 0149 opt.showDecorationSelected = false; 0150 opt.state &= ~QStyle::State_Selected; 0151 0152 QItemDelegate::paint(painter, opt, index); 0153 } 0154 0155 //* size hint for index 0156 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override 0157 { 0158 // get size from either proxy or parent class 0159 auto size(_proxy ? _proxy.data()->sizeHint(option, index) : QItemDelegate::sizeHint(option, index)); 0160 0161 // adjust and return 0162 if (size.isValid()) { 0163 size.rheight() += _itemMargin * 2; 0164 } 0165 return size; 0166 } 0167 0168 private: 0169 //* proxy 0170 Breeze::WeakPointer<QAbstractItemDelegate> _proxy; 0171 0172 //* margin 0173 int _itemMargin; 0174 }; 0175 0176 //_______________________________________________________________ 0177 bool isProgressBarHorizontal(const QStyleOptionProgressBar *option) 0178 { 0179 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0180 return option && (option->state & QStyle::State_Horizontal); 0181 #else 0182 return option && ((option->state & QStyle::State_Horizontal) || option->orientation == Qt::Horizontal); 0183 #endif 0184 } 0185 0186 enum class ToolButtonMenuArrowStyle { 0187 None, 0188 InlineLarge, 0189 InlineSmall, 0190 SubControl, 0191 }; 0192 0193 ToolButtonMenuArrowStyle toolButtonMenuArrowStyle(const QStyleOption *option) 0194 { 0195 const auto toolButtonOption = qstyleoption_cast<const QStyleOptionToolButton *>(option); 0196 if (!toolButtonOption) { 0197 return ToolButtonMenuArrowStyle::None; 0198 } 0199 0200 const bool hasPopupMenu(toolButtonOption->features & QStyleOptionToolButton::HasMenu 0201 && toolButtonOption->features & QStyleOptionToolButton::MenuButtonPopup); 0202 const bool hasInlineIndicator(toolButtonOption->features & QStyleOptionToolButton::HasMenu && !hasPopupMenu); 0203 const bool hasDelayedMenu(hasInlineIndicator && toolButtonOption->features & QStyleOptionToolButton::PopupDelay); 0204 0205 const bool hasIcon = !toolButtonOption->icon.isNull() || (toolButtonOption->features & QStyleOptionToolButton::Arrow); 0206 const bool iconOnly = toolButtonOption->toolButtonStyle == Qt::ToolButtonIconOnly || (toolButtonOption->text.isEmpty() && hasIcon); 0207 0208 if (hasPopupMenu) { 0209 return ToolButtonMenuArrowStyle::SubControl; 0210 } 0211 0212 if (hasDelayedMenu) { 0213 return ToolButtonMenuArrowStyle::InlineSmall; 0214 } 0215 0216 if (hasInlineIndicator && !iconOnly) { 0217 return ToolButtonMenuArrowStyle::InlineLarge; 0218 } 0219 0220 return ToolButtonMenuArrowStyle::None; 0221 } 0222 0223 } 0224 0225 namespace Breeze 0226 { 0227 //______________________________________________________________ 0228 Style::Style() 0229 : 0230 0231 _helper(new Helper(StyleConfigData::self()->sharedConfig())) 0232 , _shadowHelper(new ShadowHelper(this, *_helper)) 0233 , _animations(new Animations(this)) 0234 , _mnemonics(new Mnemonics(this)) 0235 , _blurHelper(new BlurHelper(this)) 0236 , _windowManager(new WindowManager(this)) 0237 , _frameShadowFactory(new FrameShadowFactory(this)) 0238 , _mdiWindowShadowFactory(new MdiWindowShadowFactory(this)) 0239 , _splitterFactory(new SplitterFactory(this)) 0240 , _toolsAreaManager(new ToolsAreaManager(_helper, this)) 0241 , _widgetExplorer(new WidgetExplorer(this)) 0242 , _tabBarData(new BreezePrivate::TabBarData(this)) 0243 #if BREEZE_HAVE_KSTYLE 0244 , SH_ArgbDndWindow(newStyleHint(QStringLiteral("SH_ArgbDndWindow"))) 0245 , CE_CapacityBar(newControlElement(QStringLiteral("CE_CapacityBar"))) 0246 #endif 0247 { 0248 // use DBus connection to update on breeze configuration change 0249 auto dbus = QDBusConnection::sessionBus(); 0250 dbus.connect(QString(), 0251 QStringLiteral("/BreezeStyle"), 0252 QStringLiteral("org.kde.Breeze.Style"), 0253 QStringLiteral("reparseConfiguration"), 0254 this, 0255 SLOT(configurationChanged())); 0256 0257 dbus.connect(QString(), 0258 QStringLiteral("/BreezeDecoration"), 0259 QStringLiteral("org.kde.Breeze.Style"), 0260 QStringLiteral("reparseConfiguration"), 0261 this, 0262 SLOT(configurationChanged())); 0263 0264 dbus.connect(QString(), 0265 QStringLiteral("/KGlobalSettings"), 0266 QStringLiteral("org.kde.KGlobalSettings"), 0267 QStringLiteral("notifyChange"), 0268 this, 0269 SLOT(configurationChanged())); 0270 0271 dbus.connect(QString(), QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig"), this, SLOT(configurationChanged())); 0272 0273 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0274 qApp->installEventFilter(this); 0275 #else 0276 connect(qApp, &QApplication::paletteChanged, this, &Style::configurationChanged); 0277 #endif 0278 0279 // call the slot directly; this initial call will set up things that also 0280 // need to be reset when the system palette changes 0281 loadConfiguration(); 0282 } 0283 0284 //______________________________________________________________ 0285 Style::~Style() 0286 { 0287 delete _shadowHelper; 0288 delete _helper; 0289 } 0290 0291 //______________________________________________________________ 0292 void Style::polish(QWidget *widget) 0293 { 0294 if (!widget) { 0295 return; 0296 } 0297 0298 // register widget to animations 0299 _animations->registerWidget(widget); 0300 _windowManager->registerWidget(widget); 0301 _frameShadowFactory->registerWidget(widget, *_helper); 0302 _mdiWindowShadowFactory->registerWidget(widget); 0303 _shadowHelper->registerWidget(widget); 0304 _splitterFactory->registerWidget(widget); 0305 _toolsAreaManager->registerWidget(widget); 0306 0307 // enable mouse over effects for all necessary widgets 0308 if (qobject_cast<QAbstractItemView *>(widget) || qobject_cast<QAbstractSpinBox *>(widget) || qobject_cast<QCheckBox *>(widget) 0309 || qobject_cast<QComboBox *>(widget) || qobject_cast<QDial *>(widget) || qobject_cast<QLineEdit *>(widget) || qobject_cast<QPushButton *>(widget) 0310 || qobject_cast<QRadioButton *>(widget) || qobject_cast<QScrollBar *>(widget) || qobject_cast<QSlider *>(widget) 0311 || qobject_cast<QSplitterHandle *>(widget) || qobject_cast<QTabBar *>(widget) || qobject_cast<QTextEdit *>(widget) 0312 || qobject_cast<QToolButton *>(widget) || widget->inherits("KTextEditor::View")) { 0313 widget->setAttribute(Qt::WA_Hover); 0314 } 0315 0316 // enforce translucency for drag and drop window 0317 if (widget->testAttribute(Qt::WA_X11NetWmWindowTypeDND) && _helper->compositingActive()) { 0318 widget->setAttribute(Qt::WA_TranslucentBackground); 0319 widget->clearMask(); 0320 } 0321 0322 // scrollarea polishing is somewhat complex. It is moved to a dedicated method 0323 polishScrollArea(qobject_cast<QAbstractScrollArea *>(widget)); 0324 0325 if (auto itemView = qobject_cast<QAbstractItemView *>(widget)) { 0326 // enable mouse over effects in itemviews' viewport 0327 itemView->viewport()->setAttribute(Qt::WA_Hover); 0328 0329 } else if (auto groupBox = qobject_cast<QGroupBox *>(widget)) { 0330 // checkable group boxes 0331 if (groupBox->isCheckable()) { 0332 groupBox->setAttribute(Qt::WA_Hover); 0333 } 0334 0335 } else if (qobject_cast<QAbstractButton *>(widget) && qobject_cast<QDockWidget *>(widget->parent())) { 0336 widget->setAttribute(Qt::WA_Hover); 0337 0338 } else if (qobject_cast<QAbstractButton *>(widget) && qobject_cast<QToolBox *>(widget->parent())) { 0339 widget->setAttribute(Qt::WA_Hover); 0340 0341 } else if (qobject_cast<QFrame *>(widget) && widget->parent() && widget->parent()->inherits("KTitleWidget")) { 0342 widget->setAutoFillBackground(false); 0343 } 0344 0345 if (qobject_cast<QScrollBar *>(widget)) { 0346 // remove opaque painting for scrollbars 0347 widget->setAttribute(Qt::WA_OpaquePaintEvent, false); 0348 0349 } else if (widget->inherits("KTextEditor::View")) { 0350 addEventFilter(widget); 0351 0352 } else if (auto toolButton = qobject_cast<QToolButton *>(widget)) { 0353 if (toolButton->autoRaise()) { 0354 // for flat toolbuttons, adjust foreground and background role accordingly 0355 widget->setBackgroundRole(QPalette::NoRole); 0356 widget->setForegroundRole(QPalette::WindowText); 0357 } 0358 0359 if (widget->parentWidget() && widget->parentWidget()->parentWidget() && widget->parentWidget()->parentWidget()->inherits("Gwenview::SideBarGroup")) { 0360 widget->setProperty(PropertyNames::toolButtonAlignment, Qt::AlignLeft); 0361 } 0362 0363 } else if (qobject_cast<QDockWidget *>(widget)) { 0364 // add event filter on dock widgets 0365 // and alter palette 0366 widget->setAutoFillBackground(false); 0367 widget->setContentsMargins({}); 0368 addEventFilter(widget); 0369 0370 } else if (qobject_cast<QMdiSubWindow *>(widget)) { 0371 widget->setAutoFillBackground(false); 0372 addEventFilter(widget); 0373 0374 } else if (qobject_cast<QToolBox *>(widget)) { 0375 widget->setBackgroundRole(QPalette::NoRole); 0376 widget->setAutoFillBackground(false); 0377 0378 } else if (widget->parentWidget() && widget->parentWidget()->parentWidget() 0379 && qobject_cast<QToolBox *>(widget->parentWidget()->parentWidget()->parentWidget())) { 0380 widget->setBackgroundRole(QPalette::NoRole); 0381 widget->setAutoFillBackground(false); 0382 widget->parentWidget()->setAutoFillBackground(false); 0383 0384 } else if (qobject_cast<QMenu *>(widget)) { 0385 setTranslucentBackground(widget); 0386 0387 if (_helper->hasAlphaChannel(widget) && StyleConfigData::menuOpacity() < 100) { 0388 _blurHelper->registerWidget(widget->window()); 0389 } 0390 0391 } else if (qobject_cast<QCommandLinkButton *>(widget)) { 0392 addEventFilter(widget); 0393 0394 } else if (auto comboBox = qobject_cast<QComboBox *>(widget)) { 0395 if (!hasParent(widget, "QWebView")) { 0396 auto itemView(comboBox->view()); 0397 if (itemView && itemView->itemDelegate() && itemView->itemDelegate()->inherits("QComboBoxDelegate")) { 0398 itemView->setItemDelegate(new BreezePrivate::ComboBoxItemDelegate(itemView)); 0399 } 0400 } 0401 0402 } else if (widget->inherits("QComboBoxPrivateContainer")) { 0403 addEventFilter(widget); 0404 setTranslucentBackground(widget); 0405 0406 } else if (widget->inherits("QTipLabel")) { 0407 setTranslucentBackground(widget); 0408 0409 } else if (widget->inherits("KMultiTabBar")) { 0410 enum class Position { 0411 Left, 0412 Right, 0413 Top, 0414 Bottom, 0415 }; 0416 0417 const Position position = static_cast<Position>(widget->property("position").toInt()); 0418 const auto splitterWidth = Metrics::Splitter_SplitterWidth; 0419 0420 int left = 0, right = 0; 0421 if ((position == Position::Left && widget->layoutDirection() == Qt::LeftToRight) 0422 || (position == Position::Right && widget->layoutDirection() == Qt::RightToLeft)) { 0423 right += splitterWidth; 0424 } else if ((position == Position::Right && widget->layoutDirection() == Qt::LeftToRight) 0425 || (position == Position::Left && widget->layoutDirection() == Qt::RightToLeft)) { 0426 left += splitterWidth; 0427 } 0428 widget->setContentsMargins(left, splitterWidth, right, splitterWidth); 0429 0430 } else if (qobject_cast<QMainWindow *>(widget)) { 0431 widget->setAttribute(Qt::WA_StyledBackground); 0432 } else if (qobject_cast<QDialogButtonBox *>(widget)) { 0433 addEventFilter(widget); 0434 } else if (qobject_cast<QDialog *>(widget)) { 0435 widget->setAttribute(Qt::WA_StyledBackground); 0436 } else if (auto pushButton = qobject_cast<QPushButton *>(widget)) { 0437 QDialog *dialog = nullptr; 0438 auto p = pushButton->parentWidget(); 0439 while (p && !p->isWindow()) { 0440 p = p->parentWidget(); 0441 if (auto d = qobject_cast<QDialog *>(p)) { 0442 dialog = d; 0443 } 0444 } 0445 // Internally, QPushButton::autoDefault can be explicitly on, 0446 // explicitly off, or automatic (enabled if in a QDialog). 0447 // If autoDefault is explicitly on and not in a dialog, 0448 // or on/automatic in a dialog and has a QDialogButtonBox parent, 0449 // explicitly enable autoDefault, else explicitly disable autoDefault. 0450 bool autoDefaultNoDialog = pushButton->autoDefault() && !dialog; 0451 bool autoDefaultInDialog = pushButton->autoDefault() && dialog; 0452 auto dialogButtonBox = qobject_cast<QDialogButtonBox *>(pushButton->parent()); 0453 pushButton->setAutoDefault(autoDefaultNoDialog || (autoDefaultInDialog && dialogButtonBox)); 0454 } 0455 if (_toolsAreaManager->hasHeaderColors()) { 0456 // style TitleWidget and Search KPageView to look the same as KDE System Settings 0457 if (widget->objectName() == QLatin1String("KPageView::TitleWidget")) { 0458 widget->setAutoFillBackground(true); 0459 widget->setPalette(_toolsAreaManager->palette()); 0460 } else if (widget->objectName() == QLatin1String("KPageView::Search")) { 0461 widget->setBackgroundRole(QPalette::Window); 0462 widget->setAutoFillBackground(true); 0463 widget->setPalette(_toolsAreaManager->palette()); 0464 } 0465 } 0466 0467 // base class polishing 0468 ParentStyleClass::polish(widget); 0469 } 0470 0471 //______________________________________________________________ 0472 void Style::polish(QApplication *application) 0473 { 0474 _toolsAreaManager->registerApplication(application); 0475 _helper->installEventFilter(application); 0476 } 0477 0478 void Style::unpolish(QApplication *application) 0479 { 0480 _helper->removeEventFilter(application); 0481 } 0482 0483 //______________________________________________________________ 0484 void Style::polishScrollArea(QAbstractScrollArea *scrollArea) 0485 { 0486 // check argument 0487 if (!scrollArea) { 0488 return; 0489 } 0490 0491 // enable mouse over effect in sunken scrollareas that support focus 0492 if (scrollArea->frameShadow() == QFrame::Sunken && scrollArea->focusPolicy() & Qt::StrongFocus) { 0493 scrollArea->setAttribute(Qt::WA_Hover); 0494 } 0495 0496 if (scrollArea->viewport() && scrollArea->inherits("KItemListContainer") && scrollArea->frameShape() == QFrame::NoFrame) { 0497 scrollArea->viewport()->setBackgroundRole(QPalette::Window); 0498 scrollArea->viewport()->setForegroundRole(QPalette::WindowText); 0499 } 0500 0501 // add event filter, to make sure proper background is rendered behind scrollbars 0502 addEventFilter(scrollArea); 0503 0504 // force side panels as flat, on option 0505 if (scrollArea->inherits("KDEPrivate::KPageListView") || scrollArea->inherits("KDEPrivate::KPageTreeView")) { 0506 scrollArea->setProperty(PropertyNames::sidePanelView, true); 0507 } 0508 0509 // for all side view panels, unbold font (design choice) 0510 if (scrollArea->property(PropertyNames::sidePanelView).toBool()) { 0511 // upbold list font 0512 auto font(scrollArea->font()); 0513 font.setBold(false); 0514 scrollArea->setFont(font); 0515 } 0516 0517 // disable autofill background for flat (== NoFrame) scrollareas, with QPalette::Window as a background 0518 // this fixes flat scrollareas placed in a tinted widget, such as groupboxes, tabwidgets or framed dock-widgets 0519 if (!(scrollArea->frameShape() == QFrame::NoFrame || scrollArea->backgroundRole() == QPalette::Window)) { 0520 return; 0521 } 0522 0523 // get viewport and check background role 0524 auto viewport(scrollArea->viewport()); 0525 if (!(viewport && viewport->backgroundRole() == QPalette::Window)) { 0526 return; 0527 } 0528 0529 // change viewport autoFill background. 0530 // do the same for all children if the background role is QPalette::Window 0531 viewport->setAutoFillBackground(false); 0532 const QList<QWidget *> children(viewport->findChildren<QWidget *>()); 0533 for (QWidget *child : children) { 0534 if (child->parent() == viewport && child->backgroundRole() == QPalette::Window) { 0535 child->setAutoFillBackground(false); 0536 } 0537 } 0538 0539 /* 0540 QTreeView animates expanding/collapsing branches. It paints them into a 0541 temp pixmap whose background is unconditionally filled with the palette's 0542 *base* color which is usually different from the window's color 0543 cf. QTreeViewPrivate::renderTreeToPixmapForAnimation() 0544 */ 0545 if (auto treeView = qobject_cast<QTreeView *>(scrollArea)) { 0546 if (treeView->isAnimated()) { 0547 QPalette pal(treeView->palette()); 0548 pal.setColor(QPalette::Active, QPalette::Base, treeView->palette().color(treeView->backgroundRole())); 0549 treeView->setPalette(pal); 0550 } 0551 } 0552 } 0553 0554 //_______________________________________________________________ 0555 void Style::unpolish(QWidget *widget) 0556 { 0557 // register widget to animations 0558 _animations->unregisterWidget(widget); 0559 _frameShadowFactory->unregisterWidget(widget); 0560 _mdiWindowShadowFactory->unregisterWidget(widget); 0561 _shadowHelper->unregisterWidget(widget); 0562 _windowManager->unregisterWidget(widget); 0563 _splitterFactory->unregisterWidget(widget); 0564 _blurHelper->unregisterWidget(widget); 0565 _toolsAreaManager->unregisterWidget(widget); 0566 0567 // remove event filter 0568 if (qobject_cast<QAbstractScrollArea *>(widget) || qobject_cast<QDockWidget *>(widget) || qobject_cast<QMdiSubWindow *>(widget) 0569 || widget->inherits("QComboBoxPrivateContainer")) { 0570 widget->removeEventFilter(this); 0571 } 0572 0573 ParentStyleClass::unpolish(widget); 0574 } 0575 0576 /// Find direct parent layout as the parentWidget()->layout() might not be 0577 /// the direct parent layout but just a parent layout. 0578 QLayout *findParentLayout(const QWidget *widget) 0579 { 0580 if (!widget->parentWidget()) { 0581 return nullptr; 0582 } 0583 0584 auto layout = widget->parentWidget()->layout(); 0585 if (!layout) { 0586 return nullptr; 0587 } 0588 0589 if (layout->indexOf(const_cast<QWidget *>(widget)) > -1) { 0590 return layout; 0591 } 0592 0593 QList<QObject *> children = layout->children(); 0594 0595 while (!children.isEmpty()) { 0596 layout = qobject_cast<QLayout *>(children.takeFirst()); 0597 if (!layout) { 0598 continue; 0599 } 0600 0601 if (layout->indexOf(const_cast<QWidget *>(widget)) > -1) { 0602 return layout; 0603 } 0604 children += layout->children(); 0605 } 0606 0607 return nullptr; 0608 } 0609 0610 //______________________________________________________________ 0611 int Style::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const 0612 { 0613 // handle special cases 0614 switch (metric) { 0615 case PM_MenuHMargin: 0616 case PM_MenuVMargin: 0617 return Metrics::MenuItem_HighlightGap; 0618 0619 // small icon size 0620 case PM_SmallIconSize: { 0621 auto iconSize = ParentStyleClass::pixelMetric(metric, option, widget); 0622 if (!isTabletMode()) { 0623 return iconSize; 0624 } 0625 0626 // in tablet mode, we try to figure out the next size and use it 0627 // see bug 455513 0628 auto metaEnum = QMetaEnum::fromType<KIconLoader::StdSizes>(); 0629 for (int i = 0; i + 1 < metaEnum.keyCount(); ++i) { 0630 if (iconSize == metaEnum.value(i)) { 0631 return metaEnum.value(i + 1); 0632 } 0633 } 0634 0635 // size is either too large or unknown, just increase it by 50% 0636 return iconSize * 3 / 2; 0637 } 0638 0639 // frame width 0640 case PM_DefaultFrameWidth: { 0641 const auto isControl = isQtQuickControl(option, widget); 0642 if (!widget && !isControl) { 0643 return 0; 0644 } 0645 if (qobject_cast<const QMenu *>(widget)) { 0646 return Metrics::Menu_FrameWidth; 0647 } 0648 if (qobject_cast<const QLineEdit *>(widget)) { 0649 return Metrics::LineEdit_FrameWidth; 0650 } else if (isControl) { 0651 const QString &elementType = option->styleObject->property("elementType").toString(); 0652 if (elementType == QLatin1String("edit") || elementType == QLatin1String("spinbox")) { 0653 return Metrics::LineEdit_FrameWidth; 0654 0655 } else if (elementType == QLatin1String("combobox")) { 0656 return Metrics::ComboBox_FrameWidth; 0657 } 0658 0659 return Metrics::Frame_FrameWidth; 0660 } 0661 0662 const auto forceFrame = widget->property(PropertyNames::forceFrame); 0663 if (forceFrame.isValid() && !forceFrame.toBool()) { 0664 return 0; 0665 } 0666 if ((forceFrame.isValid() && forceFrame.toBool()) || widget->property(PropertyNames::bordersSides).isValid()) { 0667 return Metrics::Frame_FrameWidth; 0668 } 0669 0670 if (qobject_cast<const QAbstractScrollArea *>(widget)) { 0671 auto layout = findParentLayout(widget); 0672 0673 if (!layout) { 0674 if (widget->parentWidget() && widget->parentWidget()->layout()) { 0675 layout = widget->parentWidget()->layout(); 0676 } 0677 } 0678 0679 if (layout) { 0680 if (layout->inherits("QDockWidgetLayout") || layout->inherits("QMainWindowLayout") || qobject_cast<const QStackedLayout *>(layout)) { 0681 return 0; 0682 } 0683 0684 if (auto grid = qobject_cast<const QGridLayout *>(layout)) { 0685 if (grid->horizontalSpacing() > 0 || grid->verticalSpacing() > 0) { 0686 return Metrics::Frame_FrameWidth; 0687 } 0688 } 0689 0690 // Add frame when scroll area is in a layout with more than an item and the 0691 // layout has some spacing. 0692 if (layout->spacing() > 0 && layout->count() > 1) { 0693 return Metrics::Frame_FrameWidth; 0694 } 0695 } 0696 } 0697 0698 if (qobject_cast<const QTabWidget *>(widget)) { 0699 return Metrics::Frame_FrameWidth; 0700 } 0701 0702 // fallback 0703 return 0; 0704 } 0705 0706 case PM_ComboBoxFrameWidth: { 0707 const auto comboBoxOption(qstyleoption_cast<const QStyleOptionComboBox *>(option)); 0708 return comboBoxOption && comboBoxOption->editable ? Metrics::LineEdit_FrameWidth : Metrics::ComboBox_FrameWidth; 0709 } 0710 0711 case PM_SpinBoxFrameWidth: 0712 return Metrics::SpinBox_FrameWidth; 0713 case PM_ToolBarFrameWidth: 0714 return Metrics::ToolBar_FrameWidth; 0715 case PM_ToolTipLabelFrameWidth: 0716 return Metrics::ToolTip_FrameWidth; 0717 0718 case PM_FocusFrameVMargin: 0719 case PM_FocusFrameHMargin: 0720 return 2; 0721 0722 // layout 0723 case PM_LayoutLeftMargin: 0724 case PM_LayoutTopMargin: 0725 case PM_LayoutRightMargin: 0726 case PM_LayoutBottomMargin: { 0727 /* 0728 * use either Child margin or TopLevel margin, 0729 * depending on widget type 0730 */ 0731 if ((option && (option->state & QStyle::State_Window)) || (widget && widget->isWindow())) { 0732 return Metrics::Layout_TopLevelMarginWidth; 0733 0734 } else if (widget && widget->inherits("KPageView")) { 0735 return 0; 0736 0737 } else { 0738 return Metrics::Layout_ChildMarginWidth; 0739 } 0740 } 0741 0742 case PM_LayoutHorizontalSpacing: 0743 return Metrics::Layout_DefaultSpacing; 0744 case PM_LayoutVerticalSpacing: 0745 return Metrics::Layout_DefaultSpacing; 0746 0747 // buttons 0748 case PM_ButtonMargin: { 0749 // needs special case for kcalc buttons, to prevent the application to set too small margins 0750 if (widget && widget->inherits("KCalcButton")) { 0751 return Metrics::Button_MarginWidth + 4; 0752 } else { 0753 return Metrics::Button_MarginWidth; 0754 } 0755 } 0756 0757 case PM_ButtonDefaultIndicator: 0758 return 0; 0759 case PM_ButtonShiftHorizontal: 0760 return 0; 0761 case PM_ButtonShiftVertical: 0762 return 0; 0763 0764 // menubars 0765 case PM_MenuBarPanelWidth: 0766 return 0; 0767 case PM_MenuBarHMargin: 0768 return 0; 0769 case PM_MenuBarVMargin: 0770 return 0; 0771 case PM_MenuBarItemSpacing: 0772 return 0; 0773 case PM_MenuDesktopFrameWidth: 0774 return 0; 0775 0776 // menu buttons 0777 case PM_MenuButtonIndicator: 0778 return Metrics::MenuButton_IndicatorWidth; 0779 0780 // toolbars 0781 case PM_ToolBarHandleExtent: 0782 return Metrics::ToolBar_HandleExtent; 0783 case PM_ToolBarSeparatorExtent: 0784 return Metrics::ToolBar_SeparatorWidth; 0785 case PM_ToolBarExtensionExtent: 0786 return pixelMetric(PM_SmallIconSize, option, widget) + 2 * Metrics::ToolButton_MarginWidth; 0787 0788 case PM_ToolBarItemMargin: 0789 return Metrics::ToolBar_ItemMargin; 0790 case PM_ToolBarItemSpacing: 0791 return Metrics::ToolBar_ItemSpacing; 0792 0793 // tabbars 0794 case PM_TabBarTabShiftVertical: 0795 return 0; 0796 case PM_TabBarTabShiftHorizontal: 0797 return 0; 0798 case PM_TabBarTabOverlap: 0799 return Metrics::TabBar_TabOverlap; 0800 case PM_TabBarBaseOverlap: 0801 return Metrics::TabBar_BaseOverlap; 0802 case PM_TabBarTabHSpace: 0803 return 2 * Metrics::TabBar_TabMarginWidth; 0804 case PM_TabBarTabVSpace: 0805 return 2 * Metrics::TabBar_TabMarginHeight; 0806 case PM_TabCloseIndicatorWidth: 0807 case PM_TabCloseIndicatorHeight: 0808 return pixelMetric(PM_SmallIconSize, option, widget); 0809 0810 // scrollbars 0811 case PM_ScrollBarExtent: 0812 return Metrics::ScrollBar_Extend; 0813 case PM_ScrollBarSliderMin: 0814 return Metrics::ScrollBar_MinSliderHeight; 0815 0816 // title bar 0817 case PM_TitleBarHeight: 0818 return 2 * Metrics::TitleBar_MarginWidth + pixelMetric(PM_SmallIconSize, option, widget); 0819 0820 // sliders 0821 case PM_SliderThickness: 0822 return Metrics::Slider_ControlThickness; 0823 case PM_SliderControlThickness: 0824 return Metrics::Slider_ControlThickness; 0825 case PM_SliderLength: 0826 return Metrics::Slider_ControlThickness; 0827 0828 // checkboxes and radio buttons 0829 case PM_IndicatorWidth: 0830 case PM_IndicatorHeight: 0831 case PM_ExclusiveIndicatorWidth: 0832 case PM_ExclusiveIndicatorHeight: 0833 return Metrics::CheckBox_Size; 0834 0835 case PM_CheckBoxLabelSpacing: 0836 case PM_RadioButtonLabelSpacing: 0837 return Metrics::CheckBox_ItemSpacing; 0838 0839 // list headers 0840 case PM_HeaderMarkSize: 0841 return Metrics::Header_ArrowSize; 0842 case PM_HeaderMargin: 0843 return Metrics::Header_MarginWidth; 0844 0845 // dock widget 0846 // return 0 here, since frame is handled directly in polish 0847 case PM_DockWidgetFrameWidth: 0848 return 0; 0849 case PM_DockWidgetTitleMargin: 0850 return Metrics::Frame_FrameWidth; 0851 case PM_DockWidgetTitleBarButtonMargin: 0852 return Metrics::ToolButton_MarginWidth; 0853 0854 case PM_SplitterWidth: 0855 return Metrics::Splitter_SplitterWidth; 0856 case PM_DockWidgetSeparatorExtent: 0857 return Metrics::Splitter_SplitterWidth; 0858 0859 // fallback 0860 default: 0861 return ParentStyleClass::pixelMetric(metric, option, widget); 0862 } 0863 } 0864 0865 //______________________________________________________________ 0866 int Style::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const 0867 { 0868 switch (hint) { 0869 case SH_RubberBand_Mask: { 0870 if (auto mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData)) { 0871 mask->region = option->rect; 0872 0873 /* 0874 * need to check on widget before removing inner region 0875 * in order to still preserve rubberband in MainWindow and QGraphicsView 0876 * in QMainWindow because it looks better 0877 * in QGraphicsView because the painting fails completely otherwise 0878 */ 0879 if (widget 0880 && (qobject_cast<const QAbstractItemView *>(widget->parent()) || qobject_cast<const QGraphicsView *>(widget->parent()) 0881 || qobject_cast<const QMainWindow *>(widget->parent()))) { 0882 return true; 0883 } 0884 0885 // also check if widget's parent is some itemView viewport 0886 if (widget && widget->parent() && qobject_cast<const QAbstractItemView *>(widget->parent()->parent()) 0887 && static_cast<const QAbstractItemView *>(widget->parent()->parent())->viewport() == widget->parent()) { 0888 return true; 0889 } 0890 0891 // mask out center 0892 mask->region -= insideMargin(option->rect, 1); 0893 0894 return true; 0895 } 0896 return false; 0897 } 0898 0899 case SH_ComboBox_ListMouseTracking: 0900 return true; 0901 case SH_MenuBar_MouseTracking: 0902 return true; 0903 case SH_Menu_Scrollable: 0904 return true; 0905 case SH_Menu_MouseTracking: 0906 return true; 0907 case SH_Menu_SubMenuPopupDelay: 0908 return 150; 0909 case SH_Menu_SloppySubMenus: 0910 return true; 0911 0912 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0913 case SH_Widget_Animate: 0914 return StyleConfigData::animationsEnabled(); 0915 #endif 0916 case SH_Menu_SupportsSections: 0917 return true; 0918 case SH_Widget_Animation_Duration: 0919 return StyleConfigData::animationsEnabled() ? StyleConfigData::animationsDuration() : 0; 0920 0921 case SH_DialogButtonBox_ButtonsHaveIcons: 0922 return true; 0923 0924 case SH_GroupBox_TextLabelVerticalAlignment: 0925 return Qt::AlignVCenter; 0926 case SH_TabBar_Alignment: 0927 return StyleConfigData::tabBarDrawCenteredTabs() ? Qt::AlignCenter : Qt::AlignLeft; 0928 case SH_ToolBox_SelectedPageTitleBold: 0929 return false; 0930 case SH_ScrollBar_MiddleClickAbsolutePosition: 0931 return true; 0932 case SH_ScrollView_FrameOnlyAroundContents: 0933 return false; 0934 case SH_FormLayoutFormAlignment: 0935 return Qt::AlignLeft | Qt::AlignTop; 0936 case SH_FormLayoutLabelAlignment: 0937 return Qt::AlignRight; 0938 case SH_FormLayoutFieldGrowthPolicy: 0939 return QFormLayout::ExpandingFieldsGrow; 0940 case SH_FormLayoutWrapPolicy: 0941 return QFormLayout::DontWrapRows; 0942 case SH_MessageBox_TextInteractionFlags: 0943 return Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse; 0944 case SH_ProgressDialog_CenterCancelButton: 0945 return false; 0946 case SH_MessageBox_CenterButtons: 0947 return false; 0948 0949 case SH_FocusFrame_AboveWidget: 0950 return true; 0951 case SH_FocusFrame_Mask: 0952 return false; 0953 0954 case SH_RequestSoftwareInputPanel: 0955 return RSIP_OnMouseClick; 0956 case SH_TitleBar_NoBorder: 0957 return true; 0958 case SH_DockWidget_ButtonsHaveFrame: 0959 return false; 0960 default: 0961 return ParentStyleClass::styleHint(hint, option, widget, returnData); 0962 } 0963 } 0964 0965 //______________________________________________________________ 0966 QRect Style::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const 0967 { 0968 switch (element) { 0969 case SE_PushButtonContents: 0970 return pushButtonContentsRect(option, widget); 0971 case SE_CheckBoxContents: 0972 return checkBoxContentsRect(option, widget); 0973 case SE_RadioButtonContents: 0974 return checkBoxContentsRect(option, widget); 0975 case SE_LineEditContents: 0976 return lineEditContentsRect(option, widget); 0977 case SE_ProgressBarGroove: 0978 return progressBarGrooveRect(option, widget); 0979 case SE_ProgressBarContents: 0980 return progressBarContentsRect(option, widget); 0981 case SE_ProgressBarLabel: 0982 return progressBarLabelRect(option, widget); 0983 case SE_FrameContents: 0984 return frameContentsRect(option, widget); 0985 case SE_HeaderArrow: 0986 return headerArrowRect(option, widget); 0987 case SE_HeaderLabel: 0988 return headerLabelRect(option, widget); 0989 case SE_TabBarTabLeftButton: 0990 return tabBarTabLeftButtonRect(option, widget); 0991 case SE_TabBarTabRightButton: 0992 return tabBarTabRightButtonRect(option, widget); 0993 case SE_TabWidgetTabBar: 0994 return tabWidgetTabBarRect(option, widget); 0995 case SE_TabWidgetTabContents: 0996 return tabWidgetTabContentsRect(option, widget); 0997 case SE_TabWidgetTabPane: 0998 return tabWidgetTabPaneRect(option, widget); 0999 case SE_TabWidgetLeftCorner: 1000 return tabWidgetCornerRect(SE_TabWidgetLeftCorner, option, widget); 1001 case SE_TabWidgetRightCorner: 1002 return tabWidgetCornerRect(SE_TabWidgetRightCorner, option, widget); 1003 case SE_ToolBoxTabContents: 1004 return toolBoxTabContentsRect(option, widget); 1005 1006 // fallback 1007 default: 1008 return ParentStyleClass::subElementRect(element, option, widget); 1009 } 1010 } 1011 1012 //______________________________________________________________ 1013 QRect Style::subControlRect(ComplexControl element, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 1014 { 1015 switch (element) { 1016 case CC_GroupBox: 1017 return groupBoxSubControlRect(option, subControl, widget); 1018 case CC_ToolButton: 1019 return toolButtonSubControlRect(option, subControl, widget); 1020 case CC_ComboBox: 1021 return comboBoxSubControlRect(option, subControl, widget); 1022 case CC_SpinBox: 1023 return spinBoxSubControlRect(option, subControl, widget); 1024 case CC_ScrollBar: 1025 return scrollBarSubControlRect(option, subControl, widget); 1026 case CC_Dial: 1027 return dialSubControlRect(option, subControl, widget); 1028 case CC_Slider: 1029 return sliderSubControlRect(option, subControl, widget); 1030 1031 // fallback 1032 default: 1033 return ParentStyleClass::subControlRect(element, option, subControl, widget); 1034 } 1035 } 1036 1037 //______________________________________________________________ 1038 QSize Style::sizeFromContents(ContentsType element, const QStyleOption *option, const QSize &size, const QWidget *widget) const 1039 { 1040 switch (element) { 1041 case CT_CheckBox: 1042 return checkBoxSizeFromContents(option, size, widget); 1043 case CT_RadioButton: 1044 return checkBoxSizeFromContents(option, size, widget); 1045 case CT_LineEdit: 1046 return lineEditSizeFromContents(option, size, widget); 1047 case CT_ComboBox: 1048 return comboBoxSizeFromContents(option, size, widget); 1049 case CT_SpinBox: 1050 return spinBoxSizeFromContents(option, size, widget); 1051 case CT_Slider: 1052 return sliderSizeFromContents(option, size, widget); 1053 case CT_PushButton: 1054 return pushButtonSizeFromContents(option, size, widget); 1055 case CT_ToolButton: 1056 return toolButtonSizeFromContents(option, size, widget); 1057 case CT_MenuBar: 1058 return defaultSizeFromContents(option, size, widget); 1059 case CT_MenuBarItem: 1060 return menuBarItemSizeFromContents(option, size, widget); 1061 case CT_MenuItem: 1062 return menuItemSizeFromContents(option, size, widget); 1063 case CT_ProgressBar: 1064 return progressBarSizeFromContents(option, size, widget); 1065 case CT_TabWidget: 1066 return tabWidgetSizeFromContents(option, size, widget); 1067 case CT_TabBarTab: 1068 return tabBarTabSizeFromContents(option, size, widget); 1069 case CT_HeaderSection: 1070 return headerSectionSizeFromContents(option, size, widget); 1071 case CT_ItemViewItem: 1072 return itemViewItemSizeFromContents(option, size, widget); 1073 1074 // fallback 1075 default: 1076 return ParentStyleClass::sizeFromContents(element, option, size, widget); 1077 } 1078 } 1079 1080 //______________________________________________________________ 1081 QStyle::SubControl Style::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, const QPoint &point, const QWidget *widget) const 1082 { 1083 switch (control) { 1084 case CC_ScrollBar: { 1085 auto grooveRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget); 1086 if (grooveRect.contains(point)) { 1087 // Must be either page up/page down, or just click on the slider. 1088 auto sliderRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); 1089 1090 if (sliderRect.contains(point)) { 1091 return SC_ScrollBarSlider; 1092 } else if (preceeds(point, sliderRect, option)) { 1093 return SC_ScrollBarSubPage; 1094 } else { 1095 return SC_ScrollBarAddPage; 1096 } 1097 } 1098 1099 // This is one of the up/down buttons. First, decide which one it is. 1100 if (preceeds(point, grooveRect, option)) { 1101 if (_subLineButtons == DoubleButton) { 1102 auto buttonRect = scrollBarInternalSubControlRect(option, SC_ScrollBarSubLine); 1103 return scrollBarHitTest(buttonRect, point, option); 1104 1105 } else { 1106 return SC_ScrollBarSubLine; 1107 } 1108 } 1109 1110 if (_addLineButtons == DoubleButton) { 1111 auto buttonRect = scrollBarInternalSubControlRect(option, SC_ScrollBarAddLine); 1112 return scrollBarHitTest(buttonRect, point, option); 1113 1114 } else { 1115 return SC_ScrollBarAddLine; 1116 } 1117 } 1118 1119 // fallback 1120 default: 1121 return ParentStyleClass::hitTestComplexControl(control, option, point, widget); 1122 } 1123 } 1124 1125 //______________________________________________________________ 1126 void Style::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const 1127 { 1128 StylePrimitive fcn; 1129 switch (element) { 1130 case PE_PanelButtonCommand: 1131 fcn = &Style::drawPanelButtonCommandPrimitive; 1132 break; 1133 case PE_PanelButtonTool: 1134 fcn = &Style::drawPanelButtonToolPrimitive; 1135 break; 1136 case PE_PanelScrollAreaCorner: 1137 fcn = &Style::drawPanelScrollAreaCornerPrimitive; 1138 break; 1139 case PE_PanelMenu: 1140 fcn = &Style::drawPanelMenuPrimitive; 1141 break; 1142 case PE_PanelTipLabel: 1143 fcn = &Style::drawPanelTipLabelPrimitive; 1144 break; 1145 case PE_PanelItemViewItem: 1146 fcn = &Style::drawPanelItemViewItemPrimitive; 1147 break; 1148 case PE_IndicatorCheckBox: 1149 fcn = &Style::drawIndicatorCheckBoxPrimitive; 1150 break; 1151 case PE_IndicatorRadioButton: 1152 fcn = &Style::drawIndicatorRadioButtonPrimitive; 1153 break; 1154 case PE_IndicatorButtonDropDown: 1155 fcn = &Style::drawIndicatorButtonDropDownPrimitive; 1156 break; 1157 case PE_IndicatorTabClose: 1158 fcn = &Style::drawIndicatorTabClosePrimitive; 1159 break; 1160 case PE_IndicatorTabTear: 1161 fcn = &Style::drawIndicatorTabTearPrimitive; 1162 break; 1163 case PE_IndicatorArrowUp: 1164 fcn = &Style::drawIndicatorArrowUpPrimitive; 1165 break; 1166 case PE_IndicatorArrowDown: 1167 fcn = &Style::drawIndicatorArrowDownPrimitive; 1168 break; 1169 case PE_IndicatorArrowLeft: 1170 fcn = &Style::drawIndicatorArrowLeftPrimitive; 1171 break; 1172 case PE_IndicatorArrowRight: 1173 fcn = &Style::drawIndicatorArrowRightPrimitive; 1174 break; 1175 case PE_IndicatorHeaderArrow: 1176 fcn = &Style::drawIndicatorHeaderArrowPrimitive; 1177 break; 1178 case PE_IndicatorToolBarHandle: 1179 fcn = &Style::drawIndicatorToolBarHandlePrimitive; 1180 break; 1181 case PE_IndicatorToolBarSeparator: 1182 fcn = &Style::drawIndicatorToolBarSeparatorPrimitive; 1183 break; 1184 case PE_IndicatorBranch: 1185 fcn = &Style::drawIndicatorBranchPrimitive; 1186 break; 1187 case PE_FrameStatusBarItem: 1188 fcn = &Style::emptyPrimitive; 1189 break; 1190 case PE_Frame: 1191 fcn = &Style::drawFramePrimitive; 1192 break; 1193 case PE_FrameLineEdit: 1194 fcn = &Style::drawFrameLineEditPrimitive; 1195 break; 1196 case PE_FrameMenu: 1197 fcn = &Style::drawFrameMenuPrimitive; 1198 break; 1199 case PE_FrameGroupBox: 1200 fcn = &Style::drawFrameGroupBoxPrimitive; 1201 break; 1202 case PE_FrameTabWidget: 1203 fcn = &Style::drawFrameTabWidgetPrimitive; 1204 break; 1205 case PE_FrameTabBarBase: 1206 fcn = &Style::drawFrameTabBarBasePrimitive; 1207 break; 1208 case PE_FrameWindow: 1209 fcn = &Style::drawFrameWindowPrimitive; 1210 break; 1211 case PE_FrameFocusRect: 1212 fcn = _frameFocusPrimitive; 1213 break; 1214 case PE_IndicatorDockWidgetResizeHandle: 1215 fcn = &Style::drawDockWidgetResizeHandlePrimitive; 1216 break; 1217 case PE_PanelStatusBar: 1218 fcn = &Style::drawPanelStatusBarPrimitive; 1219 break; 1220 case PE_Widget: 1221 fcn = &Style::drawWidgetPrimitive; 1222 break; 1223 1224 // fallback 1225 default: 1226 break; 1227 } 1228 1229 painter->save(); 1230 1231 // call function if implemented 1232 if (!(fcn && fcn(*this, option, painter, widget))) { 1233 ParentStyleClass::drawPrimitive(element, option, painter, widget); 1234 } 1235 1236 painter->restore(); 1237 } 1238 1239 bool Style::drawWidgetPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 1240 { 1241 Q_UNUSED(option) 1242 const auto drawBackground = _toolsAreaManager->hasHeaderColors() && _helper->shouldDrawToolsArea(widget); 1243 1244 auto mw = qobject_cast<const QMainWindow *>(widget); 1245 if (mw && mw == mw->window()) { 1246 painter->save(); 1247 1248 auto rect = _toolsAreaManager->toolsAreaRect(mw); 1249 1250 if (rect.height() == 0) { 1251 if (mw->property(PropertyNames::noSeparator).toBool() || mw->isFullScreen()) { 1252 painter->restore(); 1253 return true; 1254 } 1255 painter->setPen(QPen(_helper->separatorColor(_toolsAreaManager->palette()), PenWidth::Frame * widget->devicePixelRatio())); 1256 painter->drawLine(widget->rect().topLeft(), widget->rect().topRight()); 1257 painter->restore(); 1258 return true; 1259 } 1260 1261 auto color = _toolsAreaManager->palette().brush(mw->isActiveWindow() ? QPalette::Active : QPalette::Inactive, QPalette::Window); 1262 1263 if (drawBackground) { 1264 painter->setPen(Qt::transparent); 1265 painter->setBrush(color); 1266 painter->drawRect(rect); 1267 } 1268 1269 painter->setPen(_helper->separatorColor(_toolsAreaManager->palette())); 1270 painter->drawLine(rect.bottomLeft(), rect.bottomRight()); 1271 1272 painter->restore(); 1273 } else if (auto dialog = qobject_cast<const QDialog *>(widget)) { 1274 if (dialog->isFullScreen()) { 1275 return true; 1276 } 1277 if (auto vLayout = qobject_cast<QVBoxLayout *>(widget->layout())) { 1278 QRect rect(0, 0, widget->width(), 0); 1279 const auto color = _toolsAreaManager->palette().brush(widget->isActiveWindow() ? QPalette::Active : QPalette::Inactive, QPalette::Window); 1280 1281 if (vLayout->menuBar()) { 1282 rect.setHeight(rect.height() + vLayout->menuBar()->rect().height()); 1283 } 1284 1285 for (int i = 0, count = vLayout->count(); i < count; i++) { 1286 const auto layoutItem = vLayout->itemAt(i); 1287 if (layoutItem->widget() && qobject_cast<QToolBar *>(layoutItem->widget())) { 1288 rect.setHeight(rect.height() + layoutItem->widget()->rect().height() + vLayout->spacing()); 1289 } else { 1290 break; 1291 } 1292 } 1293 1294 if (rect.height() > 0) { 1295 // We found either a QMenuBar or a QToolBar 1296 1297 // Add contentsMargins + separator 1298 rect.setHeight(rect.height() + widget->devicePixelRatio() + vLayout->contentsMargins().top()); 1299 1300 if (drawBackground) { 1301 painter->setPen(Qt::transparent); 1302 painter->setBrush(color); 1303 painter->drawRect(rect); 1304 } 1305 1306 painter->setPen(QPen(_helper->separatorColor(_toolsAreaManager->palette()), widget->devicePixelRatio())); 1307 painter->drawLine(rect.bottomLeft(), rect.bottomRight()); 1308 1309 return true; 1310 } 1311 } 1312 1313 painter->setPen(QPen(_helper->separatorColor(_toolsAreaManager->palette()), PenWidth::Frame * widget->devicePixelRatio())); 1314 painter->drawLine(widget->rect().topLeft(), widget->rect().topRight()); 1315 } else if (widget && widget->inherits("KMultiTabBar")) { 1316 enum class Position { 1317 Left, 1318 Right, 1319 Top, 1320 Bottom, 1321 }; 1322 1323 const Position position = static_cast<Position>(widget->property("position").toInt()); 1324 const auto splitterWidth = Metrics::Splitter_SplitterWidth; 1325 QRect rect = option->rect; 1326 1327 if (position == Position::Top || position == Position::Bottom) { 1328 return true; 1329 } 1330 1331 if ((position == Position::Left && widget->layoutDirection() == Qt::LeftToRight) 1332 || (position == Position::Right && widget->layoutDirection() == Qt::RightToLeft)) { 1333 rect.setX(rect.width() - splitterWidth); 1334 } 1335 1336 rect.setWidth(splitterWidth); 1337 1338 const auto color(_helper->separatorColor(option->palette)); 1339 _helper->renderSeparator(painter, rect, color, true); 1340 } 1341 return true; 1342 } 1343 1344 //______________________________________________________________ 1345 void Style::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const 1346 { 1347 StyleControl fcn; 1348 1349 #if BREEZE_HAVE_KSTYLE 1350 if (element == CE_CapacityBar) { 1351 fcn = &Style::drawProgressBarControl; 1352 1353 } else 1354 #endif 1355 { 1356 switch (element) { 1357 case CE_PushButtonBevel: 1358 fcn = &Style::drawPanelButtonCommandPrimitive; 1359 break; 1360 case CE_PushButtonLabel: 1361 fcn = &Style::drawPushButtonLabelControl; 1362 break; 1363 case CE_CheckBoxLabel: 1364 fcn = &Style::drawCheckBoxLabelControl; 1365 break; 1366 case CE_RadioButtonLabel: 1367 fcn = &Style::drawCheckBoxLabelControl; 1368 break; 1369 case CE_ToolButtonLabel: 1370 fcn = &Style::drawToolButtonLabelControl; 1371 break; 1372 case CE_ComboBoxLabel: 1373 fcn = &Style::drawComboBoxLabelControl; 1374 break; 1375 case CE_MenuBarEmptyArea: 1376 fcn = &Style::emptyControl; 1377 break; 1378 case CE_MenuBarItem: 1379 fcn = &Style::drawMenuBarItemControl; 1380 break; 1381 case CE_MenuItem: 1382 fcn = &Style::drawMenuItemControl; 1383 break; 1384 case CE_ToolBar: 1385 fcn = &Style::emptyControl; 1386 break; 1387 case CE_ProgressBar: 1388 fcn = &Style::drawProgressBarControl; 1389 break; 1390 case CE_ProgressBarContents: 1391 fcn = &Style::drawProgressBarContentsControl; 1392 break; 1393 case CE_ProgressBarGroove: 1394 fcn = &Style::drawProgressBarGrooveControl; 1395 break; 1396 case CE_ProgressBarLabel: 1397 fcn = &Style::drawProgressBarLabelControl; 1398 break; 1399 case CE_ScrollBarSlider: 1400 fcn = &Style::drawScrollBarSliderControl; 1401 break; 1402 case CE_ScrollBarAddLine: 1403 fcn = &Style::drawScrollBarAddLineControl; 1404 break; 1405 case CE_ScrollBarSubLine: 1406 fcn = &Style::drawScrollBarSubLineControl; 1407 break; 1408 case CE_ScrollBarAddPage: 1409 fcn = &Style::emptyControl; 1410 break; 1411 case CE_ScrollBarSubPage: 1412 fcn = &Style::emptyControl; 1413 break; 1414 case CE_ShapedFrame: 1415 fcn = &Style::drawShapedFrameControl; 1416 break; 1417 case CE_FocusFrame: 1418 fcn = &Style::drawFocusFrame; 1419 break; 1420 case CE_RubberBand: 1421 fcn = &Style::drawRubberBandControl; 1422 break; 1423 case CE_SizeGrip: 1424 fcn = &Style::emptyControl; 1425 break; 1426 case CE_HeaderSection: 1427 fcn = &Style::drawHeaderSectionControl; 1428 break; 1429 case CE_HeaderEmptyArea: 1430 fcn = &Style::drawHeaderEmptyAreaControl; 1431 break; 1432 case CE_TabBarTabLabel: 1433 fcn = &Style::drawTabBarTabLabelControl; 1434 break; 1435 case CE_TabBarTabShape: 1436 fcn = &Style::drawTabBarTabShapeControl; 1437 break; 1438 case CE_ToolBoxTabLabel: 1439 fcn = &Style::drawToolBoxTabLabelControl; 1440 break; 1441 case CE_ToolBoxTabShape: 1442 fcn = &Style::drawToolBoxTabShapeControl; 1443 break; 1444 case CE_DockWidgetTitle: 1445 fcn = &Style::drawDockWidgetTitleControl; 1446 break; 1447 case QStyle::CE_Splitter: 1448 fcn = &Style::drawSplitterControl; 1449 break; 1450 1451 // fallback 1452 default: 1453 break; 1454 } 1455 } 1456 1457 painter->save(); 1458 1459 // call function if implemented 1460 if (!(fcn && fcn(*this, option, painter, widget))) { 1461 ParentStyleClass::drawControl(element, option, painter, widget); 1462 } 1463 1464 painter->restore(); 1465 } 1466 1467 //______________________________________________________________ 1468 void Style::drawComplexControl(ComplexControl element, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 1469 { 1470 StyleComplexControl fcn; 1471 switch (element) { 1472 case CC_GroupBox: 1473 fcn = &Style::drawGroupBoxComplexControl; 1474 break; 1475 case CC_ToolButton: 1476 fcn = &Style::drawToolButtonComplexControl; 1477 break; 1478 case CC_ComboBox: 1479 fcn = &Style::drawComboBoxComplexControl; 1480 break; 1481 case CC_SpinBox: 1482 fcn = &Style::drawSpinBoxComplexControl; 1483 break; 1484 case CC_Slider: 1485 fcn = &Style::drawSliderComplexControl; 1486 break; 1487 case CC_Dial: 1488 fcn = &Style::drawDialComplexControl; 1489 break; 1490 case CC_ScrollBar: 1491 fcn = &Style::drawScrollBarComplexControl; 1492 break; 1493 case CC_TitleBar: 1494 fcn = &Style::drawTitleBarComplexControl; 1495 break; 1496 1497 // fallback 1498 default: 1499 break; 1500 } 1501 1502 painter->save(); 1503 1504 // call function if implemented 1505 if (!(fcn && fcn(*this, option, painter, widget))) { 1506 ParentStyleClass::drawComplexControl(element, option, painter, widget); 1507 } 1508 1509 painter->restore(); 1510 } 1511 1512 //___________________________________________________________________________________ 1513 void Style::drawItemText(QPainter *painter, 1514 const QRect &rect, 1515 int flags, 1516 const QPalette &palette, 1517 bool enabled, 1518 const QString &text, 1519 QPalette::ColorRole textRole) const 1520 { 1521 // hide mnemonics if requested 1522 if (!_mnemonics->enabled() && (flags & Qt::TextShowMnemonic) && !(flags & Qt::TextHideMnemonic)) { 1523 flags &= ~Qt::TextShowMnemonic; 1524 flags |= Qt::TextHideMnemonic; 1525 } 1526 1527 // make sure vertical alignment is defined 1528 // fallback on Align::VCenter if not 1529 if (!(flags & Qt::AlignVertical_Mask)) { 1530 flags |= Qt::AlignVCenter; 1531 } 1532 1533 if (_animations->widgetEnabilityEngine().enabled()) { 1534 /* 1535 * check if painter engine is registered to WidgetEnabilityEngine, and animated 1536 * if yes, merge the palettes. Note: void * is used here because we only care 1537 * about the pointer value which is used a as a key to lookup a value in a map 1538 */ 1539 const void *key = painter->device(); 1540 if (_animations->widgetEnabilityEngine().isAnimated(key, AnimationEnable)) { 1541 const QPalette copy(_helper->disabledPalette(palette, _animations->widgetEnabilityEngine().opacity(key, AnimationEnable))); 1542 return ParentStyleClass::drawItemText(painter, rect, flags, copy, enabled, text, textRole); 1543 } 1544 } 1545 1546 // fallback 1547 return ParentStyleClass::drawItemText(painter, rect, flags, palette, enabled, text, textRole); 1548 } 1549 1550 bool Style::event(QEvent *e) 1551 { 1552 // Adapted from QMacStyle::event() 1553 if (e->type() == QEvent::FocusIn) { 1554 QWidget *target = nullptr; 1555 auto focusWidget = QApplication::focusWidget(); 1556 if (auto graphicsView = qobject_cast<QGraphicsView *>(focusWidget)) { 1557 QGraphicsItem *focusItem = graphicsView->scene() ? graphicsView->scene()->focusItem() : nullptr; 1558 if (focusItem && focusItem->type() == QGraphicsProxyWidget::Type) { 1559 auto proxy = static_cast<QGraphicsProxyWidget *>(focusItem); 1560 if (proxy->widget()) { 1561 focusWidget = proxy->widget()->focusWidget(); 1562 } 1563 } 1564 } 1565 1566 if (focusWidget) { 1567 auto focusEvent = static_cast<QFocusEvent *>(e); 1568 auto focusReason = focusEvent->reason(); 1569 bool hasKeyboardFocusReason = focusReason == Qt::TabFocusReason || focusReason == Qt::BacktabFocusReason || focusReason == Qt::ShortcutFocusReason; 1570 if (hasKeyboardFocusReason) { 1571 auto focusProxy = focusWidget->focusProxy(); 1572 while (focusProxy != nullptr) { 1573 focusWidget = focusProxy; 1574 focusProxy = focusWidget->focusProxy(); 1575 } 1576 // by default we want to draw a focus frame only for the following widgets 1577 if (focusWidget->inherits("QLineEdit") || focusWidget->inherits("QTextEdit") || focusWidget->inherits("QAbstractSpinBox") 1578 || focusWidget->inherits("QComboBox") || focusWidget->inherits("QPushButton") || focusWidget->inherits("QToolButton") 1579 || focusWidget->inherits("QCheckBox") || focusWidget->inherits("QRadioButton") || focusWidget->inherits("QSlider") 1580 || focusWidget->inherits("QDial") || focusWidget->inherits("QGroupBox")) { 1581 target = focusWidget; 1582 } 1583 } 1584 } 1585 1586 if (_focusFrame) { 1587 // sets to nullptr or a widget 1588 _focusFrame->setWidget(target); 1589 } else if (target) { // only create if there is a widget 1590 _focusFrame = new QFocusFrame(target); 1591 _focusFrame->setWidget(target); 1592 } 1593 } else if (e->type() == QEvent::FocusOut) { 1594 if (_focusFrame) { 1595 _focusFrame->setWidget(nullptr); 1596 } 1597 } 1598 return ParentStyleClass::event(e); 1599 } 1600 1601 //_____________________________________________________________________ 1602 bool Style::eventFilter(QObject *object, QEvent *event) 1603 { 1604 if (auto dockWidget = qobject_cast<QDockWidget *>(object)) { 1605 return eventFilterDockWidget(dockWidget, event); 1606 } else if (auto subWindow = qobject_cast<QMdiSubWindow *>(object)) { 1607 return eventFilterMdiSubWindow(subWindow, event); 1608 } else if (auto commandLinkButton = qobject_cast<QCommandLinkButton *>(object)) { 1609 return eventFilterCommandLinkButton(commandLinkButton, event); 1610 } 1611 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 1612 else if (object == qApp && event->type() == QEvent::PaletteChange) { 1613 configurationChanged(); 1614 } 1615 #endif 1616 1617 if (object->isWidgetType()) { 1618 QWidget *widget = static_cast<QWidget *>(object); 1619 1620 if (auto dialogButtonBox = qobject_cast<QDialogButtonBox *>(object)) { 1621 if (widget->property(PropertyNames::forceFrame).toBool() || (widget->parentWidget() && widget->parentWidget()->inherits("KPageView"))) { 1622 // QDialogButtonBox has no paintEvent 1623 return eventFilterDialogButtonBox(dialogButtonBox, event); 1624 } 1625 } else if (widget->inherits("QAbstractScrollArea") || widget->inherits("KTextEditor::View")) { 1626 return eventFilterScrollArea(widget, event); 1627 } else if (widget->inherits("QComboBoxPrivateContainer")) { 1628 return eventFilterComboBoxContainer(widget, event); 1629 } 1630 } 1631 1632 // fallback 1633 return ParentStyleClass::eventFilter(object, event); 1634 } 1635 1636 //____________________________________________________________________________ 1637 bool Style::eventFilterDialogButtonBox(QDialogButtonBox *widget, QEvent *event) 1638 { 1639 if (event->type() == QEvent::Paint) { 1640 QPainter painter(widget); 1641 auto paintEvent = static_cast<QPaintEvent *>(event); 1642 painter.setClipRegion(paintEvent->region()); 1643 1644 // store rect and palette 1645 auto rect(widget->rect()); 1646 rect.setHeight(1); 1647 const auto &palette(widget->palette()); 1648 1649 // define color and render 1650 const auto color(_helper->separatorColor(palette)); 1651 _helper->renderSeparator(&painter, rect, color, false); 1652 } 1653 1654 return false; 1655 } 1656 1657 //____________________________________________________________________________ 1658 bool Style::eventFilterScrollArea(QWidget *widget, QEvent *event) 1659 { 1660 switch (event->type()) { 1661 case QEvent::Paint: { 1662 // get scrollarea viewport 1663 auto scrollArea(qobject_cast<QAbstractScrollArea *>(widget)); 1664 QWidget *viewport; 1665 if (!(scrollArea && (viewport = scrollArea->viewport()))) { 1666 break; 1667 } 1668 1669 // get scrollarea horizontal and vertical containers 1670 QWidget *child(nullptr); 1671 QList<QWidget *> children; 1672 if ((child = scrollArea->findChild<QWidget *>("qt_scrollarea_vcontainer")) && child->isVisible()) { 1673 children.append(child); 1674 } 1675 1676 if ((child = scrollArea->findChild<QWidget *>("qt_scrollarea_hcontainer")) && child->isVisible()) { 1677 children.append(child); 1678 } 1679 1680 if (children.empty()) { 1681 break; 1682 } 1683 if (!scrollArea->styleSheet().isEmpty()) { 1684 break; 1685 } 1686 1687 // make sure proper background is rendered behind the containers 1688 QPainter painter(scrollArea); 1689 painter.setClipRegion(static_cast<QPaintEvent *>(event)->region()); 1690 1691 painter.setPen(Qt::NoPen); 1692 1693 // decide background color 1694 const QPalette::ColorRole role(viewport->backgroundRole()); 1695 QColor background; 1696 if (role == QPalette::Window && hasAlteredBackground(viewport)) { 1697 background = _helper->frameBackgroundColor(viewport->palette()); 1698 } else { 1699 background = viewport->palette().color(role); 1700 } 1701 painter.setBrush(background); 1702 1703 // render 1704 for (auto *child : std::as_const(children)) { 1705 painter.drawRect(child->geometry()); 1706 } 1707 1708 } break; 1709 1710 case QEvent::MouseButtonPress: 1711 case QEvent::MouseButtonRelease: 1712 case QEvent::MouseMove: { 1713 // case event 1714 QMouseEvent *mouseEvent(static_cast<QMouseEvent *>(event)); 1715 1716 // get frame framewidth 1717 const int frameWidth(pixelMetric(PM_DefaultFrameWidth, nullptr, widget)); 1718 1719 // find list of scrollbars 1720 QList<QScrollBar *> scrollBars; 1721 if (auto scrollArea = qobject_cast<QAbstractScrollArea *>(widget)) { 1722 if (scrollArea->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) { 1723 scrollBars.append(scrollArea->horizontalScrollBar()); 1724 } 1725 if (scrollArea->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) { 1726 scrollBars.append(scrollArea->verticalScrollBar()); 1727 } 1728 1729 } else if (widget->inherits("KTextEditor::View")) { 1730 scrollBars = widget->findChildren<QScrollBar *>(); 1731 } 1732 1733 // loop over found scrollbars 1734 for (QScrollBar *scrollBar : std::as_const(scrollBars)) { 1735 if (!(scrollBar && scrollBar->isVisible())) { 1736 continue; 1737 } 1738 1739 QPoint offset; 1740 if (scrollBar->orientation() == Qt::Horizontal) { 1741 offset = QPoint(0, frameWidth); 1742 } else { 1743 offset = QPoint(QApplication::isLeftToRight() ? frameWidth : -frameWidth, 0); 1744 } 1745 1746 // map position to scrollarea 1747 QPoint position(scrollBar->mapFrom(widget, mouseEvent->pos() - offset)); 1748 1749 // check if contains 1750 if (!scrollBar->rect().contains(position)) { 1751 continue; 1752 } 1753 1754 // copy event, send and return 1755 QMouseEvent copy(mouseEvent->type(), 1756 position, 1757 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) 1758 QCursor::pos(), 1759 #endif 1760 mouseEvent->button(), 1761 mouseEvent->buttons(), 1762 mouseEvent->modifiers()); 1763 1764 QCoreApplication::sendEvent(scrollBar, ©); 1765 event->setAccepted(true); 1766 return true; 1767 } 1768 1769 break; 1770 } 1771 1772 default: 1773 break; 1774 } 1775 1776 return ParentStyleClass::eventFilter(widget, event); 1777 } 1778 1779 //_________________________________________________________ 1780 bool Style::eventFilterComboBoxContainer(QWidget *widget, QEvent *event) 1781 { 1782 if (event->type() == QEvent::Paint) { 1783 QPainter painter(widget); 1784 auto paintEvent = static_cast<QPaintEvent *>(event); 1785 painter.setClipRegion(paintEvent->region()); 1786 1787 const auto rect(widget->rect()); 1788 const auto &palette(widget->palette()); 1789 const auto background(_helper->frameBackgroundColor(palette)); 1790 const auto outline(_helper->frameOutlineColor(palette)); 1791 1792 const bool hasAlpha(_helper->hasAlphaChannel(widget)); 1793 if (hasAlpha) { 1794 painter.setCompositionMode(QPainter::CompositionMode_Source); 1795 } 1796 _helper->renderMenuFrame(&painter, rect, background, outline, hasAlpha); 1797 } 1798 1799 return false; 1800 } 1801 1802 //____________________________________________________________________________ 1803 bool Style::eventFilterDockWidget(QDockWidget *dockWidget, QEvent *event) 1804 { 1805 if (event->type() == QEvent::Paint) { 1806 // create painter and clip 1807 QPainter painter(dockWidget); 1808 QPaintEvent *paintEvent = static_cast<QPaintEvent *>(event); 1809 painter.setClipRegion(paintEvent->region()); 1810 1811 // store palette and set colors 1812 const auto &palette(dockWidget->palette()); 1813 const auto background(_helper->frameBackgroundColor(palette)); 1814 const auto outline(_helper->frameOutlineColor(palette)); 1815 1816 // store rect 1817 const auto rect(dockWidget->rect()); 1818 1819 // render 1820 if (dockWidget->isFloating()) { 1821 _helper->renderMenuFrame(&painter, rect, background, outline, false); 1822 } 1823 } 1824 1825 return false; 1826 } 1827 1828 //____________________________________________________________________________ 1829 bool Style::eventFilterMdiSubWindow(QMdiSubWindow *subWindow, QEvent *event) 1830 { 1831 if (event->type() == QEvent::Paint) { 1832 QPainter painter(subWindow); 1833 QPaintEvent *paintEvent(static_cast<QPaintEvent *>(event)); 1834 painter.setClipRegion(paintEvent->region()); 1835 1836 const auto rect(subWindow->rect()); 1837 const auto background(subWindow->palette().color(QPalette::Window)); 1838 1839 if (subWindow->isMaximized()) { 1840 // full painting 1841 painter.setPen(Qt::NoPen); 1842 painter.setBrush(background); 1843 painter.drawRect(rect); 1844 1845 } else { 1846 // framed painting 1847 _helper->renderMenuFrame(&painter, rect, background, QColor()); 1848 } 1849 } 1850 1851 // continue with normal painting 1852 return false; 1853 } 1854 1855 //____________________________________________________________________________ 1856 bool Style::eventFilterCommandLinkButton(QCommandLinkButton *button, QEvent *event) 1857 { 1858 if (event->type() == QEvent::Paint) { 1859 // painter 1860 QPainter painter(button); 1861 painter.setClipRegion(static_cast<QPaintEvent *>(event)->region()); 1862 1863 const bool isFlat = false; 1864 1865 // option 1866 QStyleOptionButton option; 1867 option.initFrom(button); 1868 option.features |= QStyleOptionButton::CommandLinkButton; 1869 if (isFlat) { 1870 option.features |= QStyleOptionButton::Flat; 1871 } 1872 option.text = QString(); 1873 option.icon = QIcon(); 1874 1875 if (button->isChecked()) { 1876 option.state |= State_On; 1877 } 1878 if (button->isDown()) { 1879 option.state |= State_Sunken; 1880 } 1881 1882 // frame 1883 drawControl(QStyle::CE_PushButton, &option, &painter, button); 1884 1885 // offset 1886 const int margin(Metrics::Button_MarginWidth + Metrics::Frame_FrameWidth); 1887 QPoint offset(margin, margin); 1888 1889 // state 1890 const State &state(option.state); 1891 const bool enabled(state & State_Enabled); 1892 1893 // icon 1894 if (!button->icon().isNull()) { 1895 const auto pixmapSize(button->icon().actualSize(button->iconSize())); 1896 const QRect pixmapRect(QPoint(offset.x(), button->description().isEmpty() ? (button->height() - pixmapSize.height()) / 2 : offset.y()), pixmapSize); 1897 const qreal dpr = painter.device() ? painter.device()->devicePixelRatioF() : qApp->devicePixelRatio(); 1898 const QPixmap pixmap(_helper->coloredIcon(button->icon(), 1899 button->palette(), 1900 pixmapSize, 1901 dpr, 1902 enabled ? QIcon::Normal : QIcon::Disabled, 1903 button->isChecked() ? QIcon::On : QIcon::Off)); 1904 drawItemPixmap(&painter, pixmapRect, Qt::AlignCenter, pixmap); 1905 1906 offset.rx() += pixmapSize.width() + Metrics::Button_ItemSpacing; 1907 } 1908 1909 // text rect 1910 QRect textRect(offset, QSize(button->size().width() - offset.x() - margin, button->size().height() - 2 * margin)); 1911 const QPalette::ColorRole textRole = QPalette::ButtonText; 1912 if (!button->text().isEmpty()) { 1913 QFont font(button->font()); 1914 font.setBold(true); 1915 painter.setFont(font); 1916 if (button->description().isEmpty()) { 1917 drawItemText(&painter, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextHideMnemonic, button->palette(), enabled, button->text(), textRole); 1918 1919 } else { 1920 drawItemText(&painter, textRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextHideMnemonic, button->palette(), enabled, button->text(), textRole); 1921 textRect.setTop(textRect.top() + QFontMetrics(font).height()); 1922 } 1923 1924 painter.setFont(button->font()); 1925 } 1926 1927 if (!button->description().isEmpty()) { 1928 drawItemText(&painter, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, button->palette(), enabled, button->description(), textRole); 1929 } 1930 1931 return true; 1932 } 1933 1934 // continue with normal painting 1935 return false; 1936 } 1937 1938 //_____________________________________________________________________ 1939 void Style::configurationChanged() 1940 { 1941 // reload 1942 StyleConfigData::self()->load(); 1943 1944 // reload configuration 1945 loadConfiguration(); 1946 } 1947 1948 //_____________________________________________________________________ 1949 void Style::loadGlobalAnimationSettings() 1950 { 1951 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 1952 const KConfigGroup cg(config, QStringLiteral("KDE")); 1953 1954 // Don't override if it isn't set by the user 1955 if (!cg.hasKey("AnimationDurationFactor")) { 1956 return; 1957 } 1958 1959 const int animationsDuration = cg.readEntry("AnimationDurationFactor", StyleConfigData::animationsDuration() / 100.0f) * 100; 1960 if (animationsDuration > 0) { 1961 StyleConfigData::setAnimationsDuration(animationsDuration); 1962 StyleConfigData::setAnimationsEnabled(true); 1963 } else { 1964 StyleConfigData::setAnimationsEnabled(false); 1965 } 1966 } 1967 1968 //_____________________________________________________________________ 1969 void Style::globalConfigurationChanged(int type, int arg) 1970 { 1971 Q_UNUSED(arg); 1972 1973 // 3 == SettingsChanged, which is manually redefined in 1974 // plasma-integration/src/platformtheme/khintssettings.h and fetched 1975 // from KGlobalConfig in kdelibs4support in plasma-desktop/kcms/*, 1976 // seems to be agreed on by everything in plasma is what sets the 1977 // animation duration 1978 if (type != 3) { 1979 return; 1980 } 1981 1982 // Reload the new values 1983 loadGlobalAnimationSettings(); 1984 1985 // reinitialize engines 1986 _animations->setupEngines(); 1987 } 1988 1989 //____________________________________________________________________ 1990 QIcon Style::standardIconImplementation(StandardPixmap standardPixmap, const QStyleOption *option, const QWidget *widget) const 1991 { 1992 // lookup cache 1993 if (_iconCache.contains(standardPixmap)) { 1994 return _iconCache.value(standardPixmap); 1995 } 1996 1997 QIcon icon; 1998 switch (standardPixmap) { 1999 case SP_TitleBarNormalButton: 2000 case SP_TitleBarMinButton: 2001 case SP_TitleBarMaxButton: 2002 case SP_TitleBarCloseButton: 2003 case SP_DockWidgetCloseButton: 2004 icon = titleBarButtonIcon(standardPixmap, option, widget); 2005 break; 2006 2007 case SP_ToolBarHorizontalExtensionButton: 2008 case SP_ToolBarVerticalExtensionButton: 2009 icon = toolBarExtensionIcon(standardPixmap, option, widget); 2010 break; 2011 2012 default: 2013 break; 2014 } 2015 2016 if (icon.isNull()) { 2017 // do not cache parent style icon, since it may change at runtime 2018 return ParentStyleClass::standardIcon(standardPixmap, option, widget); 2019 2020 } else { 2021 const_cast<IconCache *>(&_iconCache)->insert(standardPixmap, icon); 2022 return icon; 2023 } 2024 } 2025 2026 //_____________________________________________________________________ 2027 void Style::loadConfiguration() 2028 { 2029 // load helper configuration 2030 _helper->loadConfig(); 2031 2032 loadGlobalAnimationSettings(); 2033 2034 // reinitialize engines 2035 _animations->setupEngines(); 2036 _windowManager->initialize(); 2037 2038 // mnemonics 2039 _mnemonics->setMode(StyleConfigData::mnemonicsMode()); 2040 2041 // splitter proxy 2042 _splitterFactory->setEnabled(StyleConfigData::splitterProxyEnabled()); 2043 2044 // reset shadow tiles 2045 _shadowHelper->loadConfig(); 2046 2047 // set mdiwindow factory shadow tiles 2048 _mdiWindowShadowFactory->setShadowHelper(_shadowHelper); 2049 2050 // clear icon cache 2051 _iconCache.clear(); 2052 2053 // scrollbar buttons 2054 switch (StyleConfigData::scrollBarAddLineButtons()) { 2055 case 0: 2056 _addLineButtons = NoButton; 2057 break; 2058 case 1: 2059 _addLineButtons = SingleButton; 2060 break; 2061 2062 default: 2063 case 2: 2064 _addLineButtons = DoubleButton; 2065 break; 2066 } 2067 2068 switch (StyleConfigData::scrollBarSubLineButtons()) { 2069 case 0: 2070 _subLineButtons = NoButton; 2071 break; 2072 case 1: 2073 _subLineButtons = SingleButton; 2074 break; 2075 2076 default: 2077 case 2: 2078 _subLineButtons = DoubleButton; 2079 break; 2080 } 2081 2082 // frame focus 2083 if (StyleConfigData::viewDrawFocusIndicator()) { 2084 _frameFocusPrimitive = &Style::drawFrameFocusRectPrimitive; 2085 } else { 2086 _frameFocusPrimitive = &Style::emptyPrimitive; 2087 } 2088 2089 // widget explorer 2090 _widgetExplorer->setEnabled(StyleConfigData::widgetExplorerEnabled()); 2091 _widgetExplorer->setDrawWidgetRects(StyleConfigData::drawWidgetRects()); 2092 } 2093 2094 //___________________________________________________________________________________________________________________ 2095 QRect Style::pushButtonContentsRect(const QStyleOption *option, const QWidget *) const 2096 { 2097 return insideMargin(option->rect, Metrics::Frame_FrameWidth); 2098 } 2099 2100 //___________________________________________________________________________________________________________________ 2101 QRect Style::checkBoxContentsRect(const QStyleOption *option, const QWidget *) const 2102 { 2103 return visualRect(option, option->rect.adjusted(Metrics::CheckBox_Size + Metrics::CheckBox_ItemSpacing, 0, 0, 0)); 2104 } 2105 2106 //___________________________________________________________________________________________________________________ 2107 QRect Style::lineEditContentsRect(const QStyleOption *option, const QWidget *widget) const 2108 { 2109 // cast option and check 2110 const auto frameOption(qstyleoption_cast<const QStyleOptionFrame *>(option)); 2111 if (!frameOption) { 2112 return option->rect; 2113 } 2114 2115 // check flatness 2116 const bool flat(frameOption->lineWidth == 0); 2117 if (flat) { 2118 return option->rect; 2119 } 2120 2121 // copy rect and take out margins 2122 auto rect(option->rect); 2123 2124 // take out margins if there is enough room 2125 const int frameWidth(pixelMetric(PM_DefaultFrameWidth, option, widget)); 2126 if (rect.height() >= option->fontMetrics.height() + 2 * frameWidth) { 2127 return insideMargin(rect, frameWidth); 2128 } else { 2129 return rect; 2130 } 2131 } 2132 2133 //___________________________________________________________________________________________________________________ 2134 QRect Style::progressBarGrooveRect(const QStyleOption *option, const QWidget *widget) const 2135 { 2136 // cast option and check 2137 const auto progressBarOption(qstyleoption_cast<const QStyleOptionProgressBar *>(option)); 2138 if (!progressBarOption) { 2139 return option->rect; 2140 } 2141 2142 // get flags and orientation 2143 const bool textVisible(progressBarOption->textVisible); 2144 const bool busy(progressBarOption->minimum == 0 && progressBarOption->maximum == 0); 2145 const bool horizontal(BreezePrivate::isProgressBarHorizontal(progressBarOption)); 2146 2147 // copy rectangle and adjust 2148 auto rect(option->rect); 2149 const int frameWidth(pixelMetric(PM_DefaultFrameWidth, option, widget)); 2150 if (horizontal) { 2151 rect = insideMargin(rect, frameWidth, 0); 2152 } else { 2153 rect = insideMargin(rect, 0, frameWidth); 2154 } 2155 2156 if (textVisible && !busy && horizontal) { 2157 auto textRect(subElementRect(SE_ProgressBarLabel, option, widget)); 2158 textRect = visualRect(option, textRect); 2159 rect.setRight(textRect.left() - Metrics::ProgressBar_ItemSpacing - 1); 2160 rect = visualRect(option, rect); 2161 rect = centerRect(rect, rect.width(), Metrics::ProgressBar_Thickness); 2162 2163 } else if (horizontal) { 2164 rect = centerRect(rect, rect.width(), Metrics::ProgressBar_Thickness); 2165 2166 } else { 2167 rect = centerRect(rect, Metrics::ProgressBar_Thickness, rect.height()); 2168 } 2169 2170 return rect; 2171 } 2172 2173 //___________________________________________________________________________________________________________________ 2174 QRect Style::progressBarContentsRect(const QStyleOption *option, const QWidget *widget) const 2175 { 2176 // cast option and check 2177 const auto progressBarOption(qstyleoption_cast<const QStyleOptionProgressBar *>(option)); 2178 if (!progressBarOption) { 2179 return QRect(); 2180 } 2181 2182 // get groove rect 2183 const auto rect(progressBarGrooveRect(option, widget)); 2184 2185 // in busy mode, grooveRect is used 2186 const bool busy(progressBarOption->minimum == 0 && progressBarOption->maximum == 0); 2187 if (busy) { 2188 return rect; 2189 } 2190 2191 // get orientation 2192 const bool horizontal(BreezePrivate::isProgressBarHorizontal(progressBarOption)); 2193 2194 // check inverted appearance 2195 bool reverse = (horizontal && (option->direction == Qt::RightToLeft)) || !horizontal; 2196 if (progressBarOption->invertedAppearance) { 2197 reverse = !reverse; 2198 } 2199 2200 // get progress and steps 2201 const int progress(progressBarOption->progress - progressBarOption->minimum); 2202 const int steps(qMax(progressBarOption->maximum - progressBarOption->minimum, 1)); 2203 2204 // Calculate width fraction 2205 const qreal position = qreal(progress) / qreal(steps); 2206 2207 // convert the pixel width 2208 const int indicatorSize(position * (horizontal ? rect.width() : rect.height())); 2209 2210 QRect indicatorRect; 2211 if (horizontal) { 2212 indicatorRect = QRect(rect.left() + (reverse ? rect.width() - indicatorSize : 0), rect.y(), indicatorSize, rect.height()); 2213 } else { 2214 indicatorRect = QRect(rect.x(), reverse ? rect.top() : (rect.bottom() - indicatorSize + 1), rect.width(), indicatorSize); 2215 } 2216 2217 return indicatorRect; 2218 } 2219 2220 //___________________________________________________________________________________________________________________ 2221 QRect Style::frameContentsRect(const QStyleOption *option, const QWidget *widget) const 2222 { 2223 if (widget) { 2224 const auto borders = widget->property(PropertyNames::bordersSides); 2225 if (borders.isValid() && borders.canConvert<Qt::Edges>()) { 2226 const auto value = borders.value<Qt::Edges>(); 2227 auto rect = option->rect; 2228 2229 if ((value & Qt::LeftEdge && widget->layoutDirection() == Qt::LeftToRight) 2230 || (value & Qt::RightEdge && widget->layoutDirection() == Qt::RightToLeft)) { 2231 rect.adjust(1, 0, 0, 0); 2232 } 2233 if ((value & Qt::RightEdge && widget->layoutDirection() == Qt::LeftToRight) 2234 || (value & Qt::LeftEdge && widget->layoutDirection() == Qt::RightToLeft)) { 2235 rect.adjust(0, 0, -1, 0); 2236 } 2237 if (value & Qt::TopEdge) { 2238 rect.adjust(0, 1, 0, 0); 2239 } 2240 if (value & Qt::BottomEdge) { 2241 rect.adjust(0, 0, 0, -1); 2242 } 2243 2244 return rect; 2245 } 2246 } 2247 2248 if (!StyleConfigData::sidePanelDrawFrame() && qobject_cast<const QAbstractScrollArea *>(widget) 2249 && widget->property(PropertyNames::sidePanelView).toBool()) { 2250 // adjust margins for sidepanel widgets 2251 return option->rect.adjusted(0, 0, -1, 0); 2252 2253 } else { 2254 // base class implementation 2255 return ParentStyleClass::subElementRect(SE_FrameContents, option, widget); 2256 } 2257 } 2258 2259 //___________________________________________________________________________________________________________________ 2260 QRect Style::progressBarLabelRect(const QStyleOption *option, const QWidget *) const 2261 { 2262 // cast option and check 2263 const auto progressBarOption(qstyleoption_cast<const QStyleOptionProgressBar *>(option)); 2264 if (!progressBarOption) { 2265 return QRect(); 2266 } 2267 2268 // get flags and check 2269 const bool textVisible(progressBarOption->textVisible); 2270 const bool busy(progressBarOption->minimum == 0 && progressBarOption->maximum == 0); 2271 if (!textVisible || busy) { 2272 return QRect(); 2273 } 2274 2275 // get direction and check 2276 const bool horizontal(BreezePrivate::isProgressBarHorizontal(progressBarOption)); 2277 if (!horizontal) { 2278 return QRect(); 2279 } 2280 2281 int textWidth = qMax(option->fontMetrics.size(_mnemonics->textFlags(), progressBarOption->text).width(), 2282 option->fontMetrics.size(_mnemonics->textFlags(), QStringLiteral("100%")).width()); 2283 2284 auto rect(insideMargin(option->rect, Metrics::Frame_FrameWidth, 0)); 2285 rect.setLeft(rect.right() - textWidth + 1); 2286 rect = visualRect(option, rect); 2287 2288 return rect; 2289 } 2290 2291 //___________________________________________________________________________________________________________________ 2292 QRect Style::headerArrowRect(const QStyleOption *option, const QWidget *) const 2293 { 2294 // cast option and check 2295 const auto headerOption(qstyleoption_cast<const QStyleOptionHeader *>(option)); 2296 if (!headerOption) { 2297 return option->rect; 2298 } 2299 2300 // check if arrow is necessary 2301 if (headerOption->sortIndicator == QStyleOptionHeader::None) { 2302 return QRect(); 2303 } 2304 2305 auto arrowRect(insideMargin(option->rect, Metrics::Header_MarginWidth)); 2306 arrowRect.setLeft(arrowRect.right() - Metrics::Header_ArrowSize + 1); 2307 2308 return visualRect(option, arrowRect); 2309 } 2310 2311 //___________________________________________________________________________________________________________________ 2312 QRect Style::headerLabelRect(const QStyleOption *option, const QWidget *) const 2313 { 2314 // cast option and check 2315 const auto headerOption(qstyleoption_cast<const QStyleOptionHeader *>(option)); 2316 if (!headerOption) { 2317 return option->rect; 2318 } 2319 2320 // check if arrow is necessary 2321 auto labelRect(insideMargin(option->rect, Metrics::Header_MarginWidth, 0)); 2322 if (headerOption->sortIndicator == QStyleOptionHeader::None) { 2323 return labelRect; 2324 } 2325 2326 labelRect.adjust(0, 0, -Metrics::Header_ArrowSize - Metrics::Header_ItemSpacing, 0); 2327 return visualRect(option, labelRect); 2328 } 2329 2330 //____________________________________________________________________ 2331 QRect Style::tabBarTabLeftButtonRect(const QStyleOption *option, const QWidget *) const 2332 { 2333 // cast option and check 2334 const auto tabOption(qstyleoption_cast<const QStyleOptionTab *>(option)); 2335 if (!tabOption || tabOption->leftButtonSize.isEmpty()) { 2336 return QRect(); 2337 } 2338 2339 const auto rect(option->rect); 2340 const QSize size(tabOption->leftButtonSize); 2341 QRect buttonRect(QPoint(0, 0), size); 2342 2343 // vertical positioning 2344 switch (tabOption->shape) { 2345 case QTabBar::RoundedNorth: 2346 case QTabBar::TriangularNorth: 2347 2348 case QTabBar::RoundedSouth: 2349 case QTabBar::TriangularSouth: 2350 buttonRect.moveLeft(rect.left() + Metrics::TabBar_TabMarginWidth); 2351 buttonRect.moveTop((rect.height() - buttonRect.height()) / 2); 2352 buttonRect = visualRect(option, buttonRect); 2353 break; 2354 2355 case QTabBar::RoundedWest: 2356 case QTabBar::TriangularWest: 2357 buttonRect.moveTop(rect.top() + ((rect.height() - buttonRect.height()) / 2)); 2358 buttonRect.moveLeft((rect.width() - buttonRect.width()) / 2); 2359 break; 2360 2361 case QTabBar::RoundedEast: 2362 case QTabBar::TriangularEast: 2363 buttonRect.moveTop(rect.top() + ((rect.height() - buttonRect.height()) / 2)); 2364 buttonRect.moveLeft((rect.width() - buttonRect.width()) / 2); 2365 break; 2366 2367 default: 2368 break; 2369 } 2370 2371 return buttonRect; 2372 } 2373 2374 //____________________________________________________________________ 2375 QRect Style::tabBarTabRightButtonRect(const QStyleOption *option, const QWidget *) const 2376 { 2377 // cast option and check 2378 const auto tabOption(qstyleoption_cast<const QStyleOptionTab *>(option)); 2379 if (!tabOption || tabOption->rightButtonSize.isEmpty()) { 2380 return QRect(); 2381 } 2382 2383 const auto rect(option->rect); 2384 const auto size(tabOption->rightButtonSize); 2385 QRect buttonRect(QPoint(0, 0), size); 2386 2387 // vertical positioning 2388 switch (tabOption->shape) { 2389 case QTabBar::RoundedNorth: 2390 case QTabBar::TriangularNorth: 2391 2392 case QTabBar::RoundedSouth: 2393 case QTabBar::TriangularSouth: 2394 buttonRect.moveRight(rect.right() - Metrics::TabBar_TabMarginWidth); 2395 buttonRect.moveTop((rect.height() - buttonRect.height()) / 2); 2396 buttonRect = visualRect(option, buttonRect); 2397 break; 2398 2399 case QTabBar::RoundedWest: 2400 case QTabBar::TriangularWest: 2401 buttonRect.moveTop(rect.top() + Metrics::TabBar_TabMarginWidth); 2402 buttonRect.moveLeft((rect.width() - buttonRect.width()) / 2); 2403 break; 2404 2405 case QTabBar::RoundedEast: 2406 case QTabBar::TriangularEast: 2407 buttonRect.moveBottom(rect.bottom() - Metrics::TabBar_TabMarginWidth); 2408 buttonRect.moveLeft((rect.width() - buttonRect.width()) / 2); 2409 break; 2410 2411 default: 2412 break; 2413 } 2414 2415 return buttonRect; 2416 } 2417 2418 //____________________________________________________________________ 2419 QRect Style::tabWidgetTabBarRect(const QStyleOption *option, const QWidget *widget) const 2420 { 2421 // cast option and check 2422 const auto tabOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option); 2423 if (!tabOption) { 2424 return ParentStyleClass::subElementRect(SE_TabWidgetTabBar, option, widget); 2425 } 2426 2427 // do nothing if tabbar is hidden 2428 const QSize tabBarSize(tabOption->tabBarSize); 2429 2430 auto rect(option->rect); 2431 QRect tabBarRect(QPoint(0, 0), tabBarSize); 2432 2433 Qt::Alignment tabBarAlignment(styleHint(SH_TabBar_Alignment, option, widget)); 2434 2435 // horizontal positioning 2436 const bool verticalTabs(isVerticalTab(tabOption->shape)); 2437 if (verticalTabs) { 2438 tabBarRect.setHeight(qMin(tabBarRect.height(), rect.height() - 2)); 2439 if (tabBarAlignment == Qt::AlignCenter) { 2440 tabBarRect.moveTop(rect.top() + (rect.height() - tabBarRect.height()) / 2); 2441 } else { 2442 tabBarRect.moveTop(rect.top() + 1); 2443 } 2444 2445 } else { 2446 // account for corner rects 2447 // need to re-run visualRect to remove right-to-left handling, since it is re-added on tabBarRect at the end 2448 const auto leftButtonRect(visualRect(option, subElementRect(SE_TabWidgetLeftCorner, option, widget))); 2449 const auto rightButtonRect(visualRect(option, subElementRect(SE_TabWidgetRightCorner, option, widget))); 2450 2451 rect.setLeft(leftButtonRect.width()); 2452 rect.setRight(rightButtonRect.left() - 1); 2453 2454 tabBarRect.setWidth(qMin(tabBarRect.width(), rect.width() - 2)); 2455 if (tabBarAlignment == Qt::AlignCenter) { 2456 tabBarRect.moveLeft(rect.left() + (rect.width() - tabBarRect.width()) / 2); 2457 } else { 2458 tabBarRect.moveLeft(rect.left() + 1); 2459 } 2460 2461 tabBarRect = visualRect(option, tabBarRect); 2462 } 2463 2464 // expand the tab bar towards the frame to cover the frame's border 2465 switch (tabOption->shape) { 2466 case QTabBar::RoundedNorth: 2467 case QTabBar::TriangularNorth: 2468 tabBarRect.moveTop(rect.top()); 2469 tabBarRect.setBottom(tabBarRect.bottom() + 1); 2470 break; 2471 2472 case QTabBar::RoundedSouth: 2473 case QTabBar::TriangularSouth: 2474 tabBarRect.moveBottom(rect.bottom()); 2475 tabBarRect.setTop(tabBarRect.top() - 1); 2476 break; 2477 2478 case QTabBar::RoundedWest: 2479 case QTabBar::TriangularWest: 2480 tabBarRect.moveLeft(rect.left()); 2481 tabBarRect.setRight(tabBarRect.right() + 1); 2482 break; 2483 2484 case QTabBar::RoundedEast: 2485 case QTabBar::TriangularEast: 2486 tabBarRect.moveRight(rect.right()); 2487 tabBarRect.setLeft(tabBarRect.left() - 1); 2488 break; 2489 2490 default: 2491 break; 2492 } 2493 2494 return tabBarRect; 2495 } 2496 2497 //____________________________________________________________________ 2498 QRect Style::tabWidgetTabContentsRect(const QStyleOption *option, const QWidget *widget) const 2499 { 2500 // cast option and check 2501 const auto tabOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option); 2502 if (!tabOption) { 2503 return option->rect; 2504 } 2505 2506 // do nothing if tabbar is hidden 2507 if (tabOption->tabBarSize.isEmpty()) { 2508 return option->rect; 2509 } 2510 const auto rect = tabWidgetTabPaneRect(option, widget); 2511 2512 const bool documentMode(tabOption->lineWidth == 0); 2513 if (documentMode) { 2514 // add margin only to the relevant side 2515 switch (tabOption->shape) { 2516 case QTabBar::RoundedNorth: 2517 case QTabBar::TriangularNorth: 2518 return rect.adjusted(0, Metrics::TabWidget_MarginWidth, 0, 0); 2519 2520 case QTabBar::RoundedSouth: 2521 case QTabBar::TriangularSouth: 2522 return rect.adjusted(0, 0, 0, -Metrics::TabWidget_MarginWidth); 2523 2524 case QTabBar::RoundedWest: 2525 case QTabBar::TriangularWest: 2526 return rect.adjusted(Metrics::TabWidget_MarginWidth, 0, 0, 0); 2527 2528 case QTabBar::RoundedEast: 2529 case QTabBar::TriangularEast: 2530 return rect.adjusted(0, 0, -Metrics::TabWidget_MarginWidth, 0); 2531 2532 default: 2533 return rect; 2534 } 2535 2536 } else { 2537 return insideMargin(rect, Metrics::TabWidget_MarginWidth); 2538 } 2539 } 2540 2541 //____________________________________________________________________ 2542 QRect Style::tabWidgetTabPaneRect(const QStyleOption *option, const QWidget *) const 2543 { 2544 const auto tabOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option); 2545 if (!tabOption || tabOption->tabBarSize.isEmpty()) { 2546 return option->rect; 2547 } 2548 2549 const int overlap = Metrics::TabBar_BaseOverlap - 1; 2550 const QSize tabBarSize(tabOption->tabBarSize - QSize(overlap, overlap)); 2551 2552 auto rect(option->rect); 2553 switch (tabOption->shape) { 2554 case QTabBar::RoundedNorth: 2555 case QTabBar::TriangularNorth: 2556 rect.adjust(0, tabBarSize.height(), 0, 0); 2557 break; 2558 2559 case QTabBar::RoundedSouth: 2560 case QTabBar::TriangularSouth: 2561 rect.adjust(0, 0, 0, -tabBarSize.height()); 2562 break; 2563 2564 case QTabBar::RoundedWest: 2565 case QTabBar::TriangularWest: 2566 rect.adjust(tabBarSize.width(), 0, 0, 0); 2567 break; 2568 2569 case QTabBar::RoundedEast: 2570 case QTabBar::TriangularEast: 2571 rect.adjust(0, 0, -tabBarSize.width(), 0); 2572 break; 2573 2574 default: 2575 return QRect(); 2576 } 2577 2578 return rect; 2579 } 2580 2581 //____________________________________________________________________ 2582 QRect Style::tabWidgetCornerRect(SubElement element, const QStyleOption *option, const QWidget *) const 2583 { 2584 // cast option and check 2585 const auto tabOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option); 2586 if (!tabOption) { 2587 return option->rect; 2588 } 2589 2590 // do nothing if tabbar is hidden 2591 const QSize tabBarSize(tabOption->tabBarSize); 2592 if (tabBarSize.isEmpty()) { 2593 return QRect(); 2594 } 2595 2596 // do nothing for vertical tabs 2597 const bool verticalTabs(isVerticalTab(tabOption->shape)); 2598 if (verticalTabs) { 2599 return QRect(); 2600 } 2601 2602 const auto rect(option->rect); 2603 QRect cornerRect; 2604 switch (element) { 2605 case SE_TabWidgetLeftCorner: 2606 cornerRect = QRect(QPoint(0, 0), tabOption->leftCornerWidgetSize); 2607 cornerRect.moveLeft(rect.left()); 2608 break; 2609 2610 case SE_TabWidgetRightCorner: 2611 cornerRect = QRect(QPoint(0, 0), tabOption->rightCornerWidgetSize); 2612 cornerRect.moveRight(rect.right()); 2613 break; 2614 2615 default: 2616 break; 2617 } 2618 2619 // expend height to tabBarSize, if needed, to make sure base is properly rendered 2620 cornerRect.setHeight(qMax(cornerRect.height(), tabBarSize.height() + 1)); 2621 2622 switch (tabOption->shape) { 2623 case QTabBar::RoundedNorth: 2624 case QTabBar::TriangularNorth: 2625 cornerRect.moveTop(rect.top()); 2626 break; 2627 2628 case QTabBar::RoundedSouth: 2629 case QTabBar::TriangularSouth: 2630 cornerRect.moveBottom(rect.bottom()); 2631 break; 2632 2633 default: 2634 break; 2635 } 2636 2637 // return cornerRect; 2638 cornerRect = visualRect(option, cornerRect); 2639 return cornerRect; 2640 } 2641 2642 //____________________________________________________________________ 2643 QRect Style::toolBoxTabContentsRect(const QStyleOption *option, const QWidget *widget) const 2644 { 2645 // cast option and check 2646 const auto toolBoxOption(qstyleoption_cast<const QStyleOptionToolBox *>(option)); 2647 if (!toolBoxOption) { 2648 return option->rect; 2649 } 2650 2651 // copy rect 2652 const auto &rect(option->rect); 2653 2654 int contentsWidth(0); 2655 if (!toolBoxOption->icon.isNull()) { 2656 const int iconSize(pixelMetric(QStyle::PM_SmallIconSize, option, widget)); 2657 contentsWidth += iconSize; 2658 2659 if (!toolBoxOption->text.isEmpty()) { 2660 contentsWidth += Metrics::ToolBox_TabItemSpacing; 2661 } 2662 } 2663 2664 if (!toolBoxOption->text.isEmpty()) { 2665 const int textWidth = toolBoxOption->fontMetrics.size(_mnemonics->textFlags(), toolBoxOption->text).width(); 2666 contentsWidth += textWidth; 2667 } 2668 2669 contentsWidth += 2 * Metrics::ToolBox_TabMarginWidth; 2670 contentsWidth = qMin(contentsWidth, rect.width()); 2671 contentsWidth = qMax(contentsWidth, int(Metrics::ToolBox_TabMinWidth)); 2672 return centerRect(rect, contentsWidth, rect.height()); 2673 } 2674 2675 //____________________________________________________________________ 2676 QRect Style::genericLayoutItemRect(const QStyleOption *option, const QWidget *) const 2677 { 2678 return insideMargin(option->rect, -Metrics::Frame_FrameWidth); 2679 } 2680 2681 //______________________________________________________________ 2682 QRect Style::groupBoxSubControlRect(const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 2683 { 2684 QRect rect = option->rect; 2685 switch (subControl) { 2686 case SC_GroupBoxFrame: 2687 return rect; 2688 2689 case SC_GroupBoxContents: { 2690 // cast option and check 2691 const auto groupBoxOption = qstyleoption_cast<const QStyleOptionGroupBox *>(option); 2692 if (!groupBoxOption) { 2693 break; 2694 } 2695 2696 // take out frame width 2697 rect = insideMargin(rect, Metrics::Frame_FrameWidth); 2698 2699 // get state 2700 const bool checkable(groupBoxOption->subControls & QStyle::SC_GroupBoxCheckBox); 2701 const bool emptyText(groupBoxOption->text.isEmpty()); 2702 2703 // calculate title height 2704 int titleHeight(0); 2705 if (!emptyText) { 2706 titleHeight = groupBoxOption->fontMetrics.height(); 2707 } 2708 if (checkable) { 2709 titleHeight = qMax(titleHeight, int(Metrics::CheckBox_Size)); 2710 } 2711 2712 // add margin 2713 if (titleHeight > 0) { 2714 titleHeight += 2 * Metrics::GroupBox_TitleMarginWidth; 2715 } 2716 2717 rect.adjust(0, titleHeight, 0, 0); 2718 return rect; 2719 } 2720 2721 case SC_GroupBoxCheckBox: 2722 case SC_GroupBoxLabel: { 2723 // cast option and check 2724 const auto groupBoxOption = qstyleoption_cast<const QStyleOptionGroupBox *>(option); 2725 if (!groupBoxOption) { 2726 break; 2727 } 2728 2729 // take out frame width 2730 rect = insideMargin(rect, Metrics::Frame_FrameWidth); 2731 2732 const bool emptyText(groupBoxOption->text.isEmpty()); 2733 const bool checkable(groupBoxOption->subControls & QStyle::SC_GroupBoxCheckBox); 2734 2735 // calculate title height 2736 int titleHeight(0); 2737 int titleWidth(0); 2738 if (!emptyText) { 2739 const QFontMetrics fontMetrics = option->fontMetrics; 2740 titleHeight = qMax(titleHeight, fontMetrics.height()); 2741 titleWidth += fontMetrics.size(_mnemonics->textFlags(), groupBoxOption->text).width(); 2742 } 2743 2744 if (checkable) { 2745 titleHeight = qMax(titleHeight, int(Metrics::CheckBox_Size)); 2746 titleWidth += Metrics::CheckBox_Size; 2747 if (!emptyText) { 2748 titleWidth += Metrics::CheckBox_ItemSpacing; 2749 } 2750 } 2751 2752 // adjust height 2753 auto titleRect(rect); 2754 titleRect.setHeight(titleHeight); 2755 titleRect.translate(0, Metrics::GroupBox_TitleMarginWidth); 2756 2757 // center 2758 titleRect = centerRect(titleRect, titleWidth, titleHeight); 2759 2760 if (subControl == SC_GroupBoxCheckBox) { 2761 // vertical centering 2762 titleRect = centerRect(titleRect, titleWidth, Metrics::CheckBox_Size); 2763 2764 // horizontal positioning 2765 const QRect subRect(titleRect.topLeft(), QSize(Metrics::CheckBox_Size, titleRect.height())); 2766 return visualRect(option->direction, titleRect, subRect); 2767 2768 } else { 2769 // vertical centering 2770 QFontMetrics fontMetrics = option->fontMetrics; 2771 titleRect = centerRect(titleRect, titleWidth, fontMetrics.height()); 2772 2773 // horizontal positioning 2774 auto subRect(titleRect); 2775 if (checkable) { 2776 subRect.adjust(Metrics::CheckBox_Size + Metrics::CheckBox_ItemSpacing, 0, 0, 0); 2777 } 2778 return visualRect(option->direction, titleRect, subRect); 2779 } 2780 } 2781 2782 default: 2783 break; 2784 } 2785 2786 return ParentStyleClass::subControlRect(CC_GroupBox, option, subControl, widget); 2787 } 2788 2789 //___________________________________________________________________________________________________________________ 2790 QRect Style::toolButtonSubControlRect(const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 2791 { 2792 // cast option and check 2793 const auto toolButtonOption = qstyleoption_cast<const QStyleOptionToolButton *>(option); 2794 if (!toolButtonOption) { 2795 return ParentStyleClass::subControlRect(CC_ToolButton, option, subControl, widget); 2796 } 2797 2798 const auto menuStyle = BreezePrivate::toolButtonMenuArrowStyle(toolButtonOption); 2799 2800 // store rect 2801 const auto &rect(option->rect); 2802 const int menuButtonWidth(Metrics::MenuButton_IndicatorWidth); 2803 switch (subControl) { 2804 case SC_ToolButtonMenu: { 2805 // check features 2806 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::None) { 2807 return QRect(); 2808 } 2809 2810 auto menuRect(rect); 2811 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::InlineSmall) { 2812 QRect arrowRect(0, 0, Metrics::SmallArrowSize, Metrics::SmallArrowSize); 2813 arrowRect.moveBottomRight(menuRect.bottomRight() - QPoint(4, 3)); 2814 menuRect = arrowRect; 2815 } else { 2816 menuRect.setLeft(rect.right() - menuButtonWidth + 1); 2817 } 2818 2819 return visualRect(option, menuRect); 2820 } 2821 2822 case SC_ToolButton: { 2823 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::SubControl) { 2824 auto contentsRect(rect); 2825 contentsRect.setRight(rect.right() - menuButtonWidth); 2826 return visualRect(option, contentsRect); 2827 2828 } else { 2829 return rect; 2830 } 2831 } 2832 2833 default: 2834 return QRect(); 2835 } 2836 } 2837 2838 //___________________________________________________________________________________________________________________ 2839 QRect Style::comboBoxSubControlRect(const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 2840 { 2841 // cast option and check 2842 const auto comboBoxOption(qstyleoption_cast<const QStyleOptionComboBox *>(option)); 2843 if (!comboBoxOption) { 2844 return ParentStyleClass::subControlRect(CC_ComboBox, option, subControl, widget); 2845 } 2846 2847 const bool editable(comboBoxOption->editable); 2848 const bool flat(editable && !comboBoxOption->frame); 2849 2850 // copy rect 2851 auto rect(option->rect); 2852 2853 switch (subControl) { 2854 case SC_ComboBoxFrame: 2855 return flat ? rect : QRect(); 2856 case SC_ComboBoxListBoxPopup: 2857 return rect; 2858 2859 case SC_ComboBoxArrow: { 2860 // take out frame width 2861 if (!flat) { 2862 rect = insideMargin(rect, Metrics::Frame_FrameWidth); 2863 } 2864 2865 QRect arrowRect(rect.right() - Metrics::MenuButton_IndicatorWidth + 1, rect.top(), Metrics::MenuButton_IndicatorWidth, rect.height()); 2866 2867 arrowRect = centerRect(arrowRect, Metrics::MenuButton_IndicatorWidth, Metrics::MenuButton_IndicatorWidth); 2868 return visualRect(option, arrowRect); 2869 } 2870 2871 case SC_ComboBoxEditField: { 2872 QRect labelRect; 2873 const int frameWidth(pixelMetric(PM_ComboBoxFrameWidth, option, widget)); 2874 labelRect = QRect(rect.left(), rect.top(), rect.width() - Metrics::MenuButton_IndicatorWidth, rect.height()); 2875 2876 // remove margins 2877 if (!flat && rect.height() >= option->fontMetrics.height() + 2 * frameWidth) { 2878 labelRect.adjust(frameWidth, frameWidth, 0, -frameWidth); 2879 } 2880 2881 return visualRect(option, labelRect); 2882 } 2883 2884 default: 2885 break; 2886 } 2887 2888 return ParentStyleClass::subControlRect(CC_ComboBox, option, subControl, widget); 2889 } 2890 2891 //___________________________________________________________________________________________________________________ 2892 QRect Style::spinBoxSubControlRect(const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 2893 { 2894 // cast option and check 2895 const auto spinBoxOption(qstyleoption_cast<const QStyleOptionSpinBox *>(option)); 2896 if (!spinBoxOption) { 2897 return ParentStyleClass::subControlRect(CC_SpinBox, option, subControl, widget); 2898 } 2899 const bool flat(!spinBoxOption->frame); 2900 2901 // copy rect 2902 auto rect(option->rect); 2903 2904 switch (subControl) { 2905 case SC_SpinBoxFrame: 2906 return flat ? QRect() : rect; 2907 2908 case SC_SpinBoxUp: 2909 case SC_SpinBoxDown: { 2910 // take out frame width 2911 if (!flat && rect.height() >= 2 * Metrics::Frame_FrameWidth + Metrics::SpinBox_ArrowButtonWidth) { 2912 rect = insideMargin(rect, Metrics::Frame_FrameWidth); 2913 } 2914 2915 QRect arrowRect; 2916 arrowRect = QRect(rect.right() - Metrics::SpinBox_ArrowButtonWidth + 1, rect.top(), Metrics::SpinBox_ArrowButtonWidth, rect.height()); 2917 2918 const int arrowHeight(qMin(rect.height(), int(Metrics::SpinBox_ArrowButtonWidth))); 2919 arrowRect = centerRect(arrowRect, Metrics::SpinBox_ArrowButtonWidth, arrowHeight); 2920 arrowRect.setHeight(arrowHeight / 2); 2921 if (subControl == SC_SpinBoxDown) { 2922 arrowRect.translate(0, arrowHeight / 2); 2923 } 2924 2925 return visualRect(option, arrowRect); 2926 } 2927 2928 case SC_SpinBoxEditField: { 2929 const bool showButtons = spinBoxOption->buttonSymbols != QAbstractSpinBox::NoButtons; 2930 2931 QRect labelRect = rect; 2932 if (showButtons) { 2933 labelRect.setRight(rect.right() - Metrics::SpinBox_ArrowButtonWidth); 2934 } 2935 2936 // remove right side line editor margins 2937 const int frameWidth(pixelMetric(PM_SpinBoxFrameWidth, option, widget)); 2938 if (!flat && labelRect.height() >= option->fontMetrics.height() + 2 * frameWidth) { 2939 labelRect.adjust(frameWidth, frameWidth, showButtons ? 0 : -frameWidth, -frameWidth); 2940 } 2941 2942 return visualRect(option, labelRect); 2943 } 2944 2945 default: 2946 break; 2947 } 2948 2949 return ParentStyleClass::subControlRect(CC_SpinBox, option, subControl, widget); 2950 } 2951 2952 //___________________________________________________________________________________________________________________ 2953 QRect Style::scrollBarInternalSubControlRect(const QStyleOptionComplex *option, SubControl subControl) const 2954 { 2955 const auto &rect = option->rect; 2956 const State &state(option->state); 2957 const bool horizontal(state & State_Horizontal); 2958 2959 switch (subControl) { 2960 case SC_ScrollBarSubLine: { 2961 int majorSize(scrollBarButtonHeight(_subLineButtons)); 2962 if (horizontal) { 2963 return visualRect(option, QRect(rect.left(), rect.top(), majorSize, rect.height())); 2964 } else { 2965 return visualRect(option, QRect(rect.left(), rect.top(), rect.width(), majorSize)); 2966 } 2967 } 2968 2969 case SC_ScrollBarAddLine: { 2970 int majorSize(scrollBarButtonHeight(_addLineButtons)); 2971 if (horizontal) { 2972 return visualRect(option, QRect(rect.right() - majorSize + 1, rect.top(), majorSize, rect.height())); 2973 } else { 2974 return visualRect(option, QRect(rect.left(), rect.bottom() - majorSize + 1, rect.width(), majorSize)); 2975 } 2976 } 2977 2978 default: 2979 return QRect(); 2980 } 2981 } 2982 2983 //___________________________________________________________________________________________________________________ 2984 QRect Style::scrollBarSubControlRect(const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 2985 { 2986 // cast option and check 2987 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 2988 if (!sliderOption) { 2989 return ParentStyleClass::subControlRect(CC_ScrollBar, option, subControl, widget); 2990 } 2991 2992 // get relevant state 2993 const State &state(option->state); 2994 const bool horizontal(state & State_Horizontal); 2995 2996 switch (subControl) { 2997 case SC_ScrollBarSubLine: 2998 case SC_ScrollBarAddLine: 2999 return scrollBarInternalSubControlRect(option, subControl); 3000 3001 case SC_ScrollBarGroove: { 3002 auto topRect = visualRect(option, scrollBarInternalSubControlRect(option, SC_ScrollBarSubLine)); 3003 auto bottomRect = visualRect(option, scrollBarInternalSubControlRect(option, SC_ScrollBarAddLine)); 3004 3005 QPoint topLeftCorner; 3006 QPoint botRightCorner; 3007 3008 if (horizontal) { 3009 topLeftCorner = QPoint(topRect.right() + 1, topRect.top()); 3010 botRightCorner = QPoint(bottomRect.left() - 1, topRect.bottom()); 3011 3012 } else { 3013 topLeftCorner = QPoint(topRect.left(), topRect.bottom() + 1); 3014 botRightCorner = QPoint(topRect.right(), bottomRect.top() - 1); 3015 } 3016 3017 // define rect 3018 return visualRect(option, QRect(topLeftCorner, botRightCorner)); 3019 } 3020 3021 case SC_ScrollBarSlider: { 3022 // handle RTL here to unreflect things if need be 3023 auto groove = visualRect(option, subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget)); 3024 3025 if (sliderOption->minimum == sliderOption->maximum) { 3026 return groove; 3027 } 3028 3029 // Figure out how much room there is 3030 int space(horizontal ? groove.width() : groove.height()); 3031 3032 // Calculate the portion of this space that the slider should occupy 3033 int sliderSize = space * qreal(sliderOption->pageStep) / (sliderOption->maximum - sliderOption->minimum + sliderOption->pageStep); 3034 sliderSize = qMax(sliderSize, static_cast<int>(Metrics::ScrollBar_MinSliderHeight)); 3035 sliderSize = qMin(sliderSize, space); 3036 3037 space -= sliderSize; 3038 if (space <= 0) { 3039 return groove; 3040 } 3041 3042 int pos = qRound(qreal(sliderOption->sliderPosition - sliderOption->minimum) / (sliderOption->maximum - sliderOption->minimum) * space); 3043 if (sliderOption->upsideDown) { 3044 pos = space - pos; 3045 } 3046 if (horizontal) { 3047 return visualRect(option, QRect(groove.left() + pos, groove.top(), sliderSize, groove.height())); 3048 } else { 3049 return visualRect(option, QRect(groove.left(), groove.top() + pos, groove.width(), sliderSize)); 3050 } 3051 } 3052 3053 case SC_ScrollBarSubPage: { 3054 // handle RTL here to unreflect things if need be 3055 auto slider = visualRect(option, subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget)); 3056 auto groove = visualRect(option, subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget)); 3057 3058 if (horizontal) { 3059 return visualRect(option, QRect(groove.left(), groove.top(), slider.left() - groove.left(), groove.height())); 3060 } else { 3061 return visualRect(option, QRect(groove.left(), groove.top(), groove.width(), slider.top() - groove.top())); 3062 } 3063 } 3064 3065 case SC_ScrollBarAddPage: { 3066 // handle RTL here to unreflect things if need be 3067 auto slider = visualRect(option, subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget)); 3068 auto groove = visualRect(option, subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget)); 3069 3070 if (horizontal) { 3071 return visualRect(option, QRect(slider.right() + 1, groove.top(), groove.right() - slider.right(), groove.height())); 3072 } else { 3073 return visualRect(option, QRect(groove.left(), slider.bottom() + 1, groove.width(), groove.bottom() - slider.bottom())); 3074 } 3075 } 3076 3077 default: 3078 return ParentStyleClass::subControlRect(CC_ScrollBar, option, subControl, widget); 3079 } 3080 } 3081 3082 //___________________________________________________________________________________________________________________ 3083 QRect Style::dialSubControlRect(const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 3084 { 3085 // cast option and check 3086 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 3087 if (!sliderOption) { 3088 return ParentStyleClass::subControlRect(CC_Dial, option, subControl, widget); 3089 } 3090 3091 // adjust rect to be square, and centered 3092 auto rect(option->rect); 3093 const int dimension(qMin(rect.width(), rect.height())); 3094 rect = centerRect(rect, dimension, dimension); 3095 3096 switch (subControl) { 3097 case QStyle::SC_DialGroove: 3098 return insideMargin(rect, (Metrics::Slider_ControlThickness - Metrics::Slider_GrooveThickness) / 2); 3099 case QStyle::SC_DialHandle: { 3100 // calculate angle at which handle needs to be drawn 3101 const qreal angle(dialAngle(sliderOption, sliderOption->sliderPosition)); 3102 3103 // groove rect 3104 const QRectF grooveRect(insideMargin(rect, Metrics::Slider_ControlThickness / 2)); 3105 qreal radius(grooveRect.width() / 2); 3106 3107 // slider center 3108 QPointF center(grooveRect.center() + QPointF(radius * std::cos(angle), -radius * std::sin(angle))); 3109 3110 // slider rect 3111 QRect handleRect(0, 0, Metrics::Slider_ControlThickness, Metrics::Slider_ControlThickness); 3112 handleRect.moveCenter(center.toPoint()); 3113 return handleRect; 3114 } 3115 3116 default: 3117 return ParentStyleClass::subControlRect(CC_Dial, option, subControl, widget); 3118 } 3119 } 3120 3121 //___________________________________________________________________________________________________________________ 3122 QRect Style::sliderSubControlRect(const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const 3123 { 3124 // cast option and check 3125 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 3126 if (!sliderOption) { 3127 return ParentStyleClass::subControlRect(CC_Slider, option, subControl, widget); 3128 } 3129 3130 // direction 3131 const bool horizontal(sliderOption->orientation == Qt::Horizontal); 3132 3133 // somewhat a copy-pasta of QCommonStyle::subControlRect, except we want 3134 // to account for our specific tick marks, and perform centering. 3135 auto rect(sliderRectWithoutTickMarks(sliderOption)); 3136 3137 switch (subControl) { 3138 case SC_SliderHandle: { 3139 QRect ret(centerRect(rect, Metrics::Slider_ControlThickness, Metrics::Slider_ControlThickness)); 3140 constexpr int len = Metrics::Slider_ControlThickness; 3141 const int sliderPos = sliderPositionFromValue(sliderOption->minimum, 3142 sliderOption->maximum, 3143 sliderOption->sliderPosition, 3144 (horizontal ? rect.width() : rect.height()) - len, 3145 sliderOption->upsideDown); 3146 if (horizontal) { 3147 ret.moveLeft(rect.x() + sliderPos); 3148 } else { 3149 ret.moveTop(rect.y() + sliderPos); 3150 } 3151 ret = visualRect(option->direction, rect, ret); 3152 return ret; 3153 } 3154 3155 case SC_SliderGroove: { 3156 auto grooveRect = insideMargin(rect, pixelMetric(PM_DefaultFrameWidth, option, widget)); 3157 3158 // centering 3159 if (horizontal) { 3160 grooveRect = centerRect(rect, grooveRect.width(), Metrics::Slider_GrooveThickness); 3161 } else { 3162 grooveRect = centerRect(rect, Metrics::Slider_GrooveThickness, grooveRect.height()); 3163 } 3164 return grooveRect; 3165 } 3166 3167 default: 3168 return ParentStyleClass::subControlRect(CC_Slider, option, subControl, widget); 3169 } 3170 } 3171 3172 //______________________________________________________________ 3173 QSize Style::checkBoxSizeFromContents(const QStyleOption *, const QSize &contentsSize, const QWidget *) const 3174 { 3175 // get contents size 3176 QSize size(contentsSize); 3177 3178 // add focus height 3179 size = expandSize(size, 0, Metrics::CheckBox_FocusMarginWidth); 3180 3181 // make sure there is enough height for indicator 3182 size.setHeight(qMax(size.height(), int(Metrics::CheckBox_Size))); 3183 3184 // Add space for the indicator and the icon 3185 size.rwidth() += Metrics::CheckBox_Size + Metrics::CheckBox_ItemSpacing; 3186 3187 // also add extra space, to leave room to the right of the label 3188 size.rwidth() += Metrics::CheckBox_ItemSpacing; 3189 3190 return size; 3191 } 3192 3193 //______________________________________________________________ 3194 QSize Style::lineEditSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const 3195 { 3196 // cast option and check 3197 const auto frameOption(qstyleoption_cast<const QStyleOptionFrame *>(option)); 3198 if (!frameOption) { 3199 return contentsSize; 3200 } 3201 3202 const bool flat(frameOption->lineWidth == 0); 3203 const int frameWidth(pixelMetric(PM_DefaultFrameWidth, option, widget)); 3204 return flat ? contentsSize : expandSize(contentsSize, frameWidth); 3205 } 3206 3207 //______________________________________________________________ 3208 QSize Style::comboBoxSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const 3209 { 3210 // cast option and check 3211 const auto comboBoxOption(qstyleoption_cast<const QStyleOptionComboBox *>(option)); 3212 if (!comboBoxOption) { 3213 return contentsSize; 3214 } 3215 3216 // copy size 3217 QSize size(contentsSize); 3218 3219 // make sure there is enough height for the button 3220 size.setHeight(qMax(size.height(), int(Metrics::MenuButton_IndicatorWidth))); 3221 3222 // add relevant margin 3223 const int frameWidth(pixelMetric(PM_ComboBoxFrameWidth, option, widget)); 3224 size = expandSize(size, frameWidth); 3225 3226 // add button width and spacing 3227 size.rwidth() += Metrics::MenuButton_IndicatorWidth + 2; 3228 size.rwidth() += Metrics::Button_ItemSpacing; 3229 3230 return size; 3231 } 3232 3233 //______________________________________________________________ 3234 QSize Style::spinBoxSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const 3235 { 3236 // cast option and check 3237 const auto spinBoxOption(qstyleoption_cast<const QStyleOptionSpinBox *>(option)); 3238 if (!spinBoxOption) { 3239 return contentsSize; 3240 } 3241 3242 const bool flat(!spinBoxOption->frame); 3243 3244 // copy size 3245 QSize size(contentsSize); 3246 3247 // add editor margins 3248 const int frameWidth(pixelMetric(PM_SpinBoxFrameWidth, option, widget)); 3249 if (!flat) { 3250 size = expandSize(size, frameWidth); 3251 } 3252 3253 // make sure there is enough height for the button 3254 size.setHeight(qMax(size.height(), int(Metrics::SpinBox_ArrowButtonWidth))); 3255 3256 // add button width and spacing 3257 const bool showButtons = spinBoxOption->buttonSymbols != QAbstractSpinBox::NoButtons; 3258 if (showButtons) { 3259 size.rwidth() += Metrics::SpinBox_ArrowButtonWidth; 3260 } 3261 3262 return size; 3263 } 3264 3265 int Style::sliderTickMarksLength() 3266 { 3267 const bool disableTicks(!StyleConfigData::sliderDrawTickMarks()); 3268 3269 /* 3270 * Qt adds its own tick length directly inside QSlider. 3271 * Take it out and replace by ours, if needed 3272 */ 3273 const int tickLength(disableTicks ? 0 3274 : (Metrics::Slider_TickLength + Metrics::Slider_TickMarginWidth 3275 + (Metrics::Slider_GrooveThickness - Metrics::Slider_ControlThickness) / 2)); 3276 constexpr int builtInTickLength(5); 3277 return tickLength - builtInTickLength; 3278 } 3279 3280 QRect Style::sliderRectWithoutTickMarks(const QStyleOptionSlider *option) 3281 { 3282 // store tick position and orientation 3283 const QSlider::TickPosition tickPosition(option->tickPosition); 3284 const bool horizontal(option->orientation == Qt::Horizontal); 3285 const int tick = sliderTickMarksLength(); 3286 3287 auto rect(option->rect); 3288 3289 if (horizontal) { 3290 if (tickPosition & QSlider::TicksAbove) { 3291 rect.setTop(-tick); 3292 } 3293 if (tickPosition & QSlider::TicksBelow) { 3294 rect.setBottom(rect.bottom() + tick); 3295 } 3296 } else { 3297 if (tickPosition & QSlider::TicksAbove) { 3298 rect.setLeft(-tick); 3299 } 3300 if (tickPosition & QSlider::TicksBelow) { 3301 rect.setRight(rect.right() + tick); 3302 } 3303 } 3304 3305 return rect; 3306 } 3307 3308 //______________________________________________________________ 3309 QSize Style::sliderSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *) const 3310 { 3311 // cast option and check 3312 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 3313 if (!sliderOption) { 3314 return contentsSize; 3315 } 3316 3317 // store tick position and orientation 3318 const QSlider::TickPosition tickPosition(sliderOption->tickPosition); 3319 const bool horizontal(sliderOption->orientation == Qt::Horizontal); 3320 const int tick = sliderTickMarksLength(); 3321 3322 // do nothing if no ticks are requested 3323 if (tickPosition == QSlider::NoTicks) { 3324 return contentsSize; 3325 } 3326 3327 QSize size(contentsSize); 3328 if (horizontal) { 3329 if (tickPosition & QSlider::TicksAbove) { 3330 size.rheight() += tick; 3331 } 3332 if (tickPosition & QSlider::TicksBelow) { 3333 size.rheight() += tick; 3334 } 3335 } else { 3336 if (tickPosition & QSlider::TicksAbove) { 3337 size.rwidth() += tick; 3338 } 3339 if (tickPosition & QSlider::TicksBelow) { 3340 size.rwidth() += tick; 3341 } 3342 } 3343 3344 return size; 3345 } 3346 3347 //______________________________________________________________ 3348 QSize Style::pushButtonSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const 3349 { 3350 // cast option and check 3351 const auto buttonOption(qstyleoption_cast<const QStyleOptionButton *>(option)); 3352 if (!buttonOption) { 3353 return contentsSize; 3354 } 3355 3356 // output 3357 QSize size; 3358 3359 // check text and icon 3360 const bool hasText(!buttonOption->text.isEmpty()); 3361 const bool flat(buttonOption->features & QStyleOptionButton::Flat); 3362 bool hasIcon(!buttonOption->icon.isNull()); 3363 3364 if (!(hasText || hasIcon)) { 3365 /* 3366 no text nor icon is passed. 3367 assume custom button and use contentsSize as a starting point 3368 */ 3369 size = contentsSize; 3370 3371 } else { 3372 /* 3373 rather than trying to guess what Qt puts into its contents size calculation, 3374 we recompute the button size entirely, based on button option 3375 this ensures consistency with the rendering stage 3376 */ 3377 3378 // update has icon to honour showIconsOnPushButtons, when possible 3379 hasIcon &= (showIconsOnPushButtons() || flat || !hasText); 3380 3381 // text 3382 if (hasText) { 3383 size = buttonOption->fontMetrics.size(Qt::TextShowMnemonic, buttonOption->text); 3384 } 3385 3386 // icon 3387 if (hasIcon) { 3388 QSize iconSize = buttonOption->iconSize; 3389 if (!iconSize.isValid()) { 3390 iconSize = QSize(pixelMetric(PM_SmallIconSize, option, widget), pixelMetric(PM_SmallIconSize, option, widget)); 3391 } 3392 3393 size.setHeight(qMax(size.height(), iconSize.height())); 3394 size.rwidth() += iconSize.width(); 3395 3396 if (hasText) { 3397 size.rwidth() += Metrics::Button_ItemSpacing; 3398 } 3399 } 3400 } 3401 3402 // menu 3403 const bool hasMenu(buttonOption->features & QStyleOptionButton::HasMenu); 3404 if (hasMenu) { 3405 size.rwidth() += Metrics::MenuButton_IndicatorWidth; 3406 if (hasText || hasIcon) { 3407 size.rwidth() += Metrics::Button_ItemSpacing; 3408 } 3409 } 3410 3411 // expand with buttons margin 3412 size = expandSize(size, Metrics::Button_MarginWidth); 3413 3414 // make sure buttons have a minimum width 3415 if (hasText) { 3416 size.setWidth(qMax(size.width(), int(Metrics::Button_MinWidth))); 3417 } 3418 3419 // finally add frame margins 3420 return expandSize(size, Metrics::Frame_FrameWidth); 3421 } 3422 3423 //______________________________________________________________ 3424 QSize Style::toolButtonSizeFromContents(const QStyleOption *option, const QSize &contentsSize, [[maybe_unused]] const QWidget *widget) const 3425 { 3426 // cast option and check 3427 const auto toolButtonOption = qstyleoption_cast<const QStyleOptionToolButton *>(option); 3428 if (!toolButtonOption) { 3429 return contentsSize; 3430 } 3431 3432 // copy size 3433 QSize size = contentsSize; 3434 3435 // get relevant state flags 3436 const State &state(option->state); 3437 const bool autoRaise(state & State_AutoRaise); 3438 3439 const auto menuStyle = BreezePrivate::toolButtonMenuArrowStyle(toolButtonOption); 3440 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::InlineLarge) { 3441 size.rwidth() += Metrics::MenuButton_IndicatorWidth; 3442 } 3443 3444 const int marginWidth(autoRaise ? Metrics::ToolButton_MarginWidth : Metrics::Button_MarginWidth + Metrics::Frame_FrameWidth); 3445 3446 size = expandSize(size, marginWidth); 3447 3448 return size; 3449 } 3450 3451 //______________________________________________________________ 3452 QSize Style::menuBarItemSizeFromContents(const QStyleOption *, const QSize &contentsSize, const QWidget *) const 3453 { 3454 return expandSize(contentsSize, Metrics::MenuBarItem_MarginWidth, Metrics::MenuBarItem_MarginHeight); 3455 } 3456 3457 //______________________________________________________________ 3458 QSize Style::menuItemSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const 3459 { 3460 // cast option and check 3461 const auto menuItemOption = qstyleoption_cast<const QStyleOptionMenuItem *>(option); 3462 if (!menuItemOption) { 3463 return contentsSize; 3464 } 3465 3466 /* 3467 * First calculate the intrinsic size of the item. 3468 * this must be kept consistent with what's in drawMenuItemControl 3469 */ 3470 switch (menuItemOption->menuItemType) { 3471 case QStyleOptionMenuItem::Normal: 3472 case QStyleOptionMenuItem::DefaultItem: 3473 case QStyleOptionMenuItem::SubMenu: { 3474 QString text = menuItemOption->text; 3475 qsizetype acceleratorSeparatorPos = text.indexOf(QLatin1Char('\t')); 3476 const bool hasAccelerator = acceleratorSeparatorPos >= 0; 3477 if (hasAccelerator) { 3478 text = text.left(acceleratorSeparatorPos); 3479 } 3480 3481 QFontMetrics fm(menuItemOption->font); 3482 QSize size = fm.boundingRect({}, Qt::TextHideMnemonic, text).size(); 3483 3484 int iconWidth = 0; 3485 if (showIconsInMenuItems()) { 3486 iconWidth = isQtQuickControl(option, widget) ? qMax(pixelMetric(PM_SmallIconSize, option, widget), menuItemOption->maxIconWidth) 3487 : menuItemOption->maxIconWidth; 3488 } 3489 3490 int leftColumnWidth = 0; 3491 3492 // add icon width 3493 if (iconWidth > 0) { 3494 leftColumnWidth += iconWidth + Metrics::MenuItem_ItemSpacing; 3495 } 3496 3497 // add checkbox indicator width 3498 if (menuItemOption->menuHasCheckableItems) { 3499 leftColumnWidth += Metrics::CheckBox_Size + Metrics::MenuItem_ItemSpacing; 3500 } 3501 3502 // add spacing for accelerator 3503 /* 3504 * Note: 3505 * The width of the accelerator itself is not included here since 3506 * Qt will add that on separately after obtaining the 3507 * sizeFromContents() for each menu item in the menu to be shown 3508 * ( see QMenuPrivate::calcActionRects() ) 3509 */ 3510 if (hasAccelerator) { 3511 size.rwidth() += Metrics::MenuItem_AcceleratorSpace; 3512 } 3513 3514 // right column 3515 const int rightColumnWidth = Metrics::MenuButton_IndicatorWidth + Metrics::MenuItem_ItemSpacing; 3516 size.rwidth() += leftColumnWidth + rightColumnWidth; 3517 3518 // make sure height is large enough for icon and arrow 3519 size.setHeight(qMax(size.height(), int(Metrics::MenuButton_IndicatorWidth))); 3520 size.setHeight(qMax(size.height(), int(Metrics::CheckBox_Size))); 3521 size.setHeight(qMax(size.height(), iconWidth)); 3522 return expandSize(size, Metrics::MenuItem_MarginWidth, (isTabletMode() ? 2 : 1) * Metrics::MenuItem_MarginHeight); 3523 } 3524 3525 case QStyleOptionMenuItem::Separator: { 3526 // contentsSize for separators in QMenuPrivate::updateActionRects() is {2,2} 3527 // We choose to override that. 3528 // Have at least 1px for separator line. 3529 int w = 1; 3530 int h = 1; 3531 3532 // If the menu item is a section, add width for text 3533 // and make height the same as other menu items, plus extra top padding. 3534 if (!menuItemOption->text.isEmpty()) { 3535 auto font = menuItemOption->font; 3536 font.setBold(true); 3537 QFontMetrics fm(font); 3538 QRect textRect = fm.boundingRect({}, Qt::TextSingleLine | Qt::TextHideMnemonic, 3539 menuItemOption->text); 3540 w = qMax(w, textRect.width()); 3541 h = qMax(h, fm.height()); 3542 3543 if (showIconsInMenuItems()) { 3544 int iconWidth = menuItemOption->maxIconWidth; 3545 if (isQtQuickControl(option, widget)) { 3546 iconWidth = qMax(pixelMetric(PM_SmallIconSize, option, widget), iconWidth); 3547 } 3548 h = qMax(h, iconWidth); 3549 } 3550 3551 if (menuItemOption->menuHasCheckableItems) { 3552 h = qMax(h, Metrics::CheckBox_Size); 3553 } 3554 3555 h = qMax(h, Metrics::MenuButton_IndicatorWidth); 3556 h += Metrics::MenuItem_MarginHeight; // extra top padding 3557 } 3558 3559 return {w + Metrics::MenuItem_MarginWidth * 2, h + Metrics::MenuItem_MarginHeight * 2}; 3560 } 3561 3562 // for all other cases, return input 3563 default: 3564 return contentsSize; 3565 } 3566 } 3567 3568 //______________________________________________________________ 3569 QSize Style::progressBarSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *) const 3570 { 3571 // cast option 3572 const auto progressBarOption(qstyleoption_cast<const QStyleOptionProgressBar *>(option)); 3573 if (!progressBarOption) { 3574 return contentsSize; 3575 } 3576 3577 const bool horizontal(BreezePrivate::isProgressBarHorizontal(progressBarOption)); 3578 3579 // make local copy 3580 QSize size(contentsSize); 3581 3582 if (horizontal) { 3583 // check text visibility 3584 const bool textVisible(progressBarOption->textVisible); 3585 3586 size.setWidth(qMax(size.width(), int(Metrics::ProgressBar_Thickness))); 3587 size.setHeight(qMax(size.height(), int(Metrics::ProgressBar_Thickness))); 3588 if (textVisible) { 3589 size.setHeight(qMax(size.height(), option->fontMetrics.height())); 3590 } 3591 3592 } else { 3593 size.setHeight(qMax(size.height(), int(Metrics::ProgressBar_Thickness))); 3594 size.setWidth(qMax(size.width(), int(Metrics::ProgressBar_Thickness))); 3595 } 3596 3597 return size; 3598 } 3599 3600 //______________________________________________________________ 3601 QSize Style::tabWidgetSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const 3602 { 3603 // cast option and check 3604 const auto tabOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option); 3605 if (!tabOption) { 3606 return expandSize(contentsSize, Metrics::TabWidget_MarginWidth); 3607 } 3608 3609 // try find direct children of type QTabBar and QStackedWidget 3610 // this is needed in order to add TabWidget margins only if they are necessary around tabWidget content, not the tabbar 3611 if (!widget) { 3612 return expandSize(contentsSize, Metrics::TabWidget_MarginWidth); 3613 } 3614 QTabBar *tabBar = nullptr; 3615 QStackedWidget *stack = nullptr; 3616 const auto children(widget->children()); 3617 for (auto child : children) { 3618 if (!tabBar) { 3619 tabBar = qobject_cast<QTabBar *>(child); 3620 } 3621 if (!stack) { 3622 stack = qobject_cast<QStackedWidget *>(child); 3623 } 3624 if (tabBar && stack) { 3625 break; 3626 } 3627 } 3628 3629 if (!(tabBar && stack)) { 3630 return expandSize(contentsSize, Metrics::TabWidget_MarginWidth); 3631 } 3632 3633 // tab orientation 3634 const bool verticalTabs(tabOption && isVerticalTab(tabOption->shape)); 3635 if (verticalTabs) { 3636 const int tabBarHeight = tabBar->minimumSizeHint().height(); 3637 const int stackHeight = stack->minimumSizeHint().height(); 3638 if (contentsSize.height() == tabBarHeight && tabBarHeight + 2 * (Metrics::Frame_FrameWidth - 1) >= stackHeight + 2 * Metrics::TabWidget_MarginWidth) { 3639 return QSize(contentsSize.width() + 2 * Metrics::TabWidget_MarginWidth, contentsSize.height() + 2 * (Metrics::Frame_FrameWidth - 1)); 3640 } else { 3641 return expandSize(contentsSize, Metrics::TabWidget_MarginWidth); 3642 } 3643 3644 } else { 3645 const int tabBarWidth = tabBar->minimumSizeHint().width(); 3646 const int stackWidth = stack->minimumSizeHint().width(); 3647 if (contentsSize.width() == tabBarWidth && tabBarWidth + 2 * (Metrics::Frame_FrameWidth - 1) >= stackWidth + 2 * Metrics::TabWidget_MarginWidth) { 3648 return QSize(contentsSize.width() + 2 * (Metrics::Frame_FrameWidth - 1), contentsSize.height() + 2 * Metrics::TabWidget_MarginWidth); 3649 } else { 3650 return expandSize(contentsSize, Metrics::TabWidget_MarginWidth); 3651 } 3652 } 3653 } 3654 3655 //______________________________________________________________ 3656 QSize Style::tabBarTabSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *) const 3657 { 3658 const auto tabOption(qstyleoption_cast<const QStyleOptionTab *>(option)); 3659 const bool hasText(tabOption && !tabOption->text.isEmpty()); 3660 const bool hasIcon(tabOption && !tabOption->icon.isNull()); 3661 const bool hasLeftButton(tabOption && !tabOption->leftButtonSize.isEmpty()); 3662 const bool hasRightButton(tabOption && !tabOption->leftButtonSize.isEmpty()); 3663 3664 // calculate width increment for horizontal tabs 3665 int widthIncrement = 0; 3666 if (hasIcon && !(hasText || hasLeftButton || hasRightButton)) { 3667 widthIncrement -= 4; 3668 } 3669 if (hasText && hasIcon) { 3670 widthIncrement += Metrics::TabBar_TabItemSpacing; 3671 } 3672 if (hasLeftButton && (hasText || hasIcon)) { 3673 widthIncrement += Metrics::TabBar_TabItemSpacing; 3674 } 3675 if (hasRightButton && (hasText || hasIcon || hasLeftButton)) { 3676 widthIncrement += Metrics::TabBar_TabItemSpacing; 3677 } 3678 3679 // add margins 3680 QSize size(contentsSize); 3681 3682 // compare to minimum size 3683 const bool verticalTabs(tabOption && isVerticalTab(tabOption)); 3684 if (verticalTabs) { 3685 size.rheight() += widthIncrement; 3686 if (hasIcon && !hasText) { 3687 size = size.expandedTo(QSize(Metrics::TabBar_TabMinHeight, 0)); 3688 } else { 3689 size = size.expandedTo(QSize(Metrics::TabBar_TabMinHeight, Metrics::TabBar_TabMinWidth)); 3690 } 3691 3692 } else { 3693 size.rwidth() += widthIncrement; 3694 if (hasIcon && !hasText) { 3695 size = size.expandedTo(QSize(0, Metrics::TabBar_TabMinHeight)); 3696 } else { 3697 size = size.expandedTo(QSize(Metrics::TabBar_TabMinWidth, Metrics::TabBar_TabMinHeight)); 3698 } 3699 } 3700 3701 return size; 3702 } 3703 3704 //______________________________________________________________ 3705 QSize Style::headerSectionSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *) const 3706 { 3707 // cast option and check 3708 const auto headerOption(qstyleoption_cast<const QStyleOptionHeader *>(option)); 3709 if (!headerOption) { 3710 return contentsSize; 3711 } 3712 3713 // get text size 3714 const bool horizontal(headerOption->orientation == Qt::Horizontal); 3715 const bool hasText(!headerOption->text.isEmpty()); 3716 const bool hasIcon(!headerOption->icon.isNull()); 3717 3718 const QSize textSize(hasText ? headerOption->fontMetrics.size(0, headerOption->text) : QSize()); 3719 const QSize iconSize(hasIcon ? QSize(22, 22) : QSize()); 3720 3721 // contents width 3722 int contentsWidth(0); 3723 if (hasText) { 3724 contentsWidth += textSize.width(); 3725 } 3726 if (hasIcon) { 3727 contentsWidth += iconSize.width(); 3728 if (hasText) { 3729 contentsWidth += Metrics::Header_ItemSpacing; 3730 } 3731 } 3732 3733 // contents height 3734 int contentsHeight(hasText ? textSize.height() : headerOption->fontMetrics.height()); 3735 if (hasIcon) { 3736 contentsHeight = qMax(contentsHeight, iconSize.height()); 3737 } 3738 3739 if (horizontal && headerOption->sortIndicator != QStyleOptionHeader::None) { 3740 // also add space for sort indicator 3741 contentsWidth += Metrics::Header_ArrowSize + Metrics::Header_ItemSpacing; 3742 contentsHeight = qMax(contentsHeight, int(Metrics::Header_ArrowSize)); 3743 } 3744 3745 // update contents size, add margins and return 3746 const QSize size(contentsSize.expandedTo(QSize(contentsWidth, contentsHeight))); 3747 return expandSize(size, Metrics::Header_MarginWidth); 3748 } 3749 3750 //______________________________________________________________ 3751 QSize Style::itemViewItemSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const 3752 { 3753 // call base class 3754 const QSize size(ParentStyleClass::sizeFromContents(CT_ItemViewItem, option, contentsSize, widget)); 3755 return expandSize(size, Metrics::ItemView_ItemMarginWidth); 3756 } 3757 3758 //______________________________________________________________ 3759 bool Style::drawFramePrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 3760 { 3761 // copy palette and rect 3762 const auto &palette(option->palette); 3763 const auto &rect(option->rect); 3764 3765 // copy state 3766 const State &state(option->state); 3767 if (!(state & (State_Sunken | State_Raised))) { 3768 return true; 3769 } 3770 3771 const bool isInputWidget((widget && widget->testAttribute(Qt::WA_Hover)) 3772 || (isQtQuickControl(option, widget) && option->styleObject->property("elementType").toString() == QStringLiteral("edit"))); 3773 3774 const bool enabled(state & State_Enabled); 3775 const bool mouseOver(enabled && isInputWidget && (state & State_MouseOver)); 3776 const bool hasFocus(enabled && isInputWidget && (state & State_HasFocus)); 3777 3778 // focus takes precedence over mouse over 3779 _animations->inputWidgetEngine().updateState(widget, AnimationFocus, hasFocus); 3780 _animations->inputWidgetEngine().updateState(widget, AnimationHover, mouseOver && !hasFocus); 3781 3782 // retrieve animation mode and opacity 3783 const AnimationMode mode(_animations->inputWidgetEngine().frameAnimationMode(widget)); 3784 const qreal opacity(_animations->inputWidgetEngine().frameOpacity(widget)); 3785 3786 if (widget && widget->property(PropertyNames::bordersSides).isValid()) { 3787 const auto background(palette.base().color()); 3788 const auto outline(_helper->frameOutlineColor(palette)); 3789 _helper->renderFrameWithSides(painter, rect, background, widget->property(PropertyNames::bordersSides).value<Qt::Edges>(), outline); 3790 3791 return true; 3792 } 3793 3794 // render 3795 if (!StyleConfigData::sidePanelDrawFrame() && widget && widget->property(PropertyNames::sidePanelView).toBool()) { 3796 const auto outline(_helper->sidePanelOutlineColor(palette, hasFocus, opacity, mode)); 3797 const bool reverseLayout(option->direction == Qt::RightToLeft); 3798 const Side side(reverseLayout ? SideRight : SideLeft); 3799 _helper->renderSidePanelFrame(painter, rect, outline, side); 3800 3801 } else { 3802 if (_frameShadowFactory->isRegistered(widget)) { 3803 // update frame shadow factory 3804 _frameShadowFactory->updateShadowsGeometry(widget, rect); 3805 _frameShadowFactory->updateState(widget, hasFocus, mouseOver, opacity, mode); 3806 } 3807 3808 const auto background(palette.base().color()); 3809 const auto outline(_helper->frameOutlineColor(palette, mouseOver, hasFocus, opacity, mode)); 3810 _helper->renderFrame(painter, rect, background, outline); 3811 } 3812 3813 return true; 3814 } 3815 3816 //______________________________________________________________ 3817 bool Style::drawFrameLineEditPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 3818 { 3819 // copy palette and rect 3820 const auto &palette(option->palette); 3821 const auto &rect(option->rect); 3822 3823 if (widget) { 3824 const auto borders = widget->property(PropertyNames::bordersSides); 3825 if (borders.isValid()) { 3826 const auto value = borders.value<Qt::Edges>(); 3827 3828 // copy state 3829 const State &state(option->state); 3830 const bool enabled(state & State_Enabled); 3831 const bool mouseOver(enabled && (state & State_MouseOver)); 3832 const bool hasFocus(enabled && (state & State_HasFocus)); 3833 3834 // Draw background 3835 const auto &background = palette.color(QPalette::Base); 3836 painter->setPen(Qt::NoPen); 3837 painter->setBrush(background); 3838 painter->drawRect(rect); 3839 3840 // Draw focus frame 3841 if (mouseOver || hasFocus) { 3842 // Retrieve animation mode and opacity 3843 const AnimationMode mode(_animations->inputWidgetEngine().frameAnimationMode(widget)); 3844 const qreal opacity(_animations->inputWidgetEngine().frameOpacity(widget)); 3845 3846 const auto outline(hasHighlightNeutral(widget, option, mouseOver, hasFocus) 3847 ? _helper->neutralText(palette) 3848 : _helper->frameOutlineColor(palette, mouseOver, hasFocus, opacity, mode)); 3849 3850 auto outlineRect = rect.adjusted(0, 0, -1, -1); 3851 if (value & Qt::LeftEdge) { 3852 outlineRect = outlineRect.adjusted(1, 0, 0, 0); 3853 } 3854 if (value & Qt::TopEdge) { 3855 outlineRect = outlineRect.adjusted(0, 1, 0, 0); 3856 } 3857 if (value & Qt::RightEdge) { 3858 outlineRect = outlineRect.adjusted(0, 0, -1, 0); 3859 } 3860 if (value & Qt::BottomEdge) { 3861 outlineRect = outlineRect.adjusted(0, 0, 0, -1); 3862 } 3863 3864 painter->setPen(outline); 3865 painter->setBrush(Qt::NoBrush); 3866 painter->drawRect(outlineRect); 3867 } 3868 3869 // Draw border 3870 const auto borderColor = _helper->frameOutlineColor(palette, false, false, 1, AnimationMode::AnimationNone); 3871 painter->setRenderHint(QPainter::Antialiasing, false); 3872 painter->setBrush(Qt::NoBrush); 3873 painter->setPen(borderColor); 3874 3875 if (value & Qt::LeftEdge) { 3876 painter->drawLine(rect.topLeft(), rect.bottomLeft()); 3877 } 3878 if (value & Qt::RightEdge) { 3879 painter->drawLine(rect.topRight(), rect.bottomRight()); 3880 } 3881 if (value & Qt::TopEdge) { 3882 painter->drawLine(rect.topLeft(), rect.topRight()); 3883 } 3884 if (value & Qt::BottomEdge) { 3885 painter->drawLine(rect.bottomLeft(), rect.bottomRight()); 3886 } 3887 3888 return true; 3889 } 3890 } 3891 3892 // make sure there is enough room to render frame 3893 if (rect.height() < 2 * Metrics::LineEdit_FrameWidth + option->fontMetrics.height()) { 3894 const auto &background = palette.color(QPalette::Base); 3895 3896 painter->setPen(Qt::NoPen); 3897 painter->setBrush(background); 3898 painter->drawRect(rect); 3899 return true; 3900 3901 } else { 3902 // copy state 3903 const State &state(option->state); 3904 const bool enabled(state & State_Enabled); 3905 const bool mouseOver(enabled && (state & State_MouseOver)); 3906 const bool hasFocus(enabled && (state & State_HasFocus)); 3907 3908 // focus takes precedence over mouse over 3909 _animations->inputWidgetEngine().updateState(widget, AnimationFocus, hasFocus); 3910 _animations->inputWidgetEngine().updateState(widget, AnimationHover, mouseOver && !hasFocus); 3911 3912 // retrieve animation mode and opacity 3913 const AnimationMode mode(_animations->inputWidgetEngine().frameAnimationMode(widget)); 3914 const qreal opacity(_animations->inputWidgetEngine().frameOpacity(widget)); 3915 3916 // render 3917 const auto &background = palette.color(QPalette::Base); 3918 const auto outline(hasHighlightNeutral(widget, option, mouseOver, hasFocus) ? _helper->neutralText(palette).lighter(mouseOver || hasFocus ? 150 : 100) 3919 : _helper->frameOutlineColor(palette, mouseOver, hasFocus, opacity, mode)); 3920 _helper->renderFrame(painter, rect, background, outline); 3921 } 3922 3923 return true; 3924 } 3925 3926 //___________________________________________________________________________________ 3927 bool Style::drawFrameFocusRectPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 3928 { 3929 // no focus indicator on buttons / scrollbars, since it is rendered elsewhere 3930 if (qobject_cast<const QAbstractButton *>(widget) || qobject_cast<const QScrollBar *>(widget) || qobject_cast<const QGroupBox *>(widget)) { 3931 return true; 3932 } 3933 3934 // no focus indicator on ComboBox list items 3935 if (widget && widget->inherits("QComboBoxListView")) { 3936 return true; 3937 } 3938 3939 if (option->styleObject && option->styleObject->property("elementType") == QLatin1String("button")) { 3940 return true; 3941 } 3942 3943 const State &state(option->state); 3944 3945 // no focus indicator on selected list items 3946 if ((state & State_Selected) && qobject_cast<const QAbstractItemView *>(widget)) { 3947 return true; 3948 } 3949 3950 const auto rect(option->rect.adjusted(0, 0, 0, 1)); 3951 const auto &palette(option->palette); 3952 3953 if (rect.width() < 10) { 3954 return true; 3955 } 3956 3957 const auto outlineColor(state & State_Selected ? palette.color(QPalette::HighlightedText) : palette.color(QPalette::Highlight)); 3958 painter->setRenderHint(QPainter::Antialiasing, false); 3959 painter->setPen(outlineColor); 3960 painter->drawLine(QPoint(rect.bottomLeft() - QPoint(0, 1)), QPoint(rect.bottomRight() - QPoint(0, 1))); 3961 3962 return true; 3963 } 3964 3965 //___________________________________________________________________________________ 3966 bool Style::drawFrameMenuPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 3967 { 3968 // only draw frame for (expanded) toolbars and QtQuick controls 3969 // do nothing for other cases, for which frame is rendered via drawPanelMenuPrimitive 3970 if (qobject_cast<const QToolBar *>(widget)) { 3971 const auto &palette(option->palette); 3972 const auto background(_helper->frameBackgroundColor(palette)); 3973 const auto outline(_helper->frameOutlineColor(palette)); 3974 3975 const bool hasAlpha(_helper->hasAlphaChannel(widget)); 3976 _helper->renderMenuFrame(painter, option->rect, background, outline, hasAlpha); 3977 3978 } else if (isQtQuickControl(option, widget)) { 3979 const auto &palette(option->palette); 3980 const auto background(_helper->frameBackgroundColor(palette)); 3981 const auto outline(_helper->frameOutlineColor(palette)); 3982 3983 const bool hasAlpha(_helper->hasAlphaChannel(widget)); 3984 _helper->renderMenuFrame(painter, option->rect, background, outline, hasAlpha); 3985 } 3986 3987 return true; 3988 } 3989 3990 //______________________________________________________________ 3991 bool Style::drawFrameGroupBoxPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *) const 3992 { 3993 // cast option and check 3994 const auto frameOption(qstyleoption_cast<const QStyleOptionFrame *>(option)); 3995 if (!frameOption) { 3996 return true; 3997 } 3998 3999 // no frame for flat groupboxes 4000 if (frameOption->features & QStyleOptionFrame::Flat) { 4001 return true; 4002 } 4003 4004 // normal frame 4005 const auto &palette(option->palette); 4006 const auto background(_helper->frameBackgroundColor(palette)); 4007 const auto outline(_helper->frameOutlineColor(palette)); 4008 4009 /* 4010 * need to reset painter's clip region in order to paint behind textbox label 4011 * (was taken out in QCommonStyle) 4012 */ 4013 4014 painter->setClipRegion(option->rect); 4015 _helper->renderFrame(painter, option->rect, background, outline); 4016 4017 return true; 4018 } 4019 4020 //___________________________________________________________________________________ 4021 bool Style::drawFrameTabWidgetPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4022 { 4023 // cast option and check 4024 const auto tabOption(qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)); 4025 if (!tabOption) { 4026 return true; 4027 } 4028 4029 // do nothing if tabbar is hidden 4030 const bool isQtQuickControl(this->isQtQuickControl(option, widget)); 4031 if (tabOption->tabBarSize.isEmpty() && !isQtQuickControl) { 4032 return true; 4033 } 4034 4035 // adjust rect to handle overlaps 4036 auto rect(option->rect); 4037 4038 const auto tabBarRect(tabOption->tabBarRect); 4039 const QSize tabBarSize(tabOption->tabBarSize); 4040 4041 // define colors 4042 const auto &palette(option->palette); 4043 const auto background(_helper->frameBackgroundColor(palette)); 4044 const auto outline(_helper->frameOutlineColor(palette)); 4045 4046 Corners corners = AllCorners; 4047 4048 // adjust corners to deal with oversized tabbars 4049 switch (tabOption->shape) { 4050 case QTabBar::RoundedNorth: 4051 case QTabBar::TriangularNorth: 4052 if (isQtQuickControl) { 4053 rect.adjust(-1, -1, 1, 0); 4054 } 4055 if (tabBarSize.width() >= rect.width() - 2 * Metrics::Frame_FrameRadius) { 4056 corners &= ~CornersTop; 4057 } 4058 if (tabBarRect.left() < rect.left() + Metrics::Frame_FrameRadius) { 4059 corners &= ~CornerTopLeft; 4060 } 4061 if (tabBarRect.right() > rect.right() - Metrics::Frame_FrameRadius) { 4062 corners &= ~CornerTopRight; 4063 } 4064 break; 4065 4066 case QTabBar::RoundedSouth: 4067 case QTabBar::TriangularSouth: 4068 if (isQtQuickControl) { 4069 rect.adjust(-1, 0, 1, 1); 4070 } 4071 if (tabBarSize.width() >= rect.width() - 2 * Metrics::Frame_FrameRadius) { 4072 corners &= ~CornersBottom; 4073 } 4074 if (tabBarRect.left() < rect.left() + Metrics::Frame_FrameRadius) { 4075 corners &= ~CornerBottomLeft; 4076 } 4077 if (tabBarRect.right() > rect.right() - Metrics::Frame_FrameRadius) { 4078 corners &= ~CornerBottomRight; 4079 } 4080 break; 4081 4082 case QTabBar::RoundedWest: 4083 case QTabBar::TriangularWest: 4084 if (isQtQuickControl) { 4085 rect.adjust(-1, 0, 0, 0); 4086 } 4087 if (tabBarSize.height() >= rect.height() - 2 * Metrics::Frame_FrameRadius) { 4088 corners &= ~CornersLeft; 4089 } 4090 if (tabBarRect.top() < rect.top() + Metrics::Frame_FrameRadius) { 4091 corners &= ~CornerTopLeft; 4092 } 4093 if (tabBarRect.bottom() > rect.bottom() - Metrics::Frame_FrameRadius) { 4094 corners &= ~CornerBottomLeft; 4095 } 4096 break; 4097 4098 case QTabBar::RoundedEast: 4099 case QTabBar::TriangularEast: 4100 if (isQtQuickControl) { 4101 rect.adjust(0, 0, 1, 0); 4102 } 4103 if (tabBarSize.height() >= rect.height() - 2 * Metrics::Frame_FrameRadius) { 4104 corners &= ~CornersRight; 4105 } 4106 if (tabBarRect.top() < rect.top() + Metrics::Frame_FrameRadius) { 4107 corners &= ~CornerTopRight; 4108 } 4109 if (tabBarRect.bottom() > rect.bottom() - Metrics::Frame_FrameRadius) { 4110 corners &= ~CornerBottomRight; 4111 } 4112 break; 4113 4114 default: 4115 break; 4116 } 4117 4118 _helper->renderTabWidgetFrame(painter, rect, background, outline, corners); 4119 4120 return true; 4121 } 4122 4123 //___________________________________________________________________________________ 4124 bool Style::drawFrameTabBarBasePrimitive(const QStyleOption *option, QPainter *painter, const QWidget *) const 4125 { 4126 // tabbar frame used either for 'separate' tabbar, or in 'document mode' 4127 4128 // cast option and check 4129 const auto tabOption(qstyleoption_cast<const QStyleOptionTabBarBase *>(option)); 4130 if (!tabOption) { 4131 return true; 4132 } 4133 4134 // get rect, orientation, palette 4135 const QRectF rect(option->rect); 4136 const auto outline(_helper->frameOutlineColor(option->palette)); 4137 4138 // setup painter 4139 painter->setBrush(Qt::NoBrush); 4140 painter->setRenderHint(QPainter::Antialiasing, false); 4141 painter->setPen(QPen(outline, PenWidth::Frame)); 4142 4143 // render 4144 switch (tabOption->shape) { 4145 case QTabBar::RoundedNorth: 4146 case QTabBar::TriangularNorth: 4147 painter->drawLine(rect.bottomLeft() - QPointF(1, 0), rect.bottomRight() + QPointF(1, 0)); 4148 break; 4149 4150 case QTabBar::RoundedSouth: 4151 case QTabBar::TriangularSouth: 4152 painter->drawLine(rect.topLeft() - QPointF(1, 0), rect.topRight() + QPointF(1, 0)); 4153 break; 4154 4155 case QTabBar::RoundedWest: 4156 case QTabBar::TriangularWest: 4157 painter->drawLine(rect.topRight() - QPointF(0, 1), rect.bottomRight() + QPointF(1, 0)); 4158 break; 4159 4160 case QTabBar::RoundedEast: 4161 case QTabBar::TriangularEast: 4162 painter->drawLine(rect.topLeft() - QPointF(0, 1), rect.bottomLeft() + QPointF(1, 0)); 4163 break; 4164 4165 default: 4166 break; 4167 } 4168 4169 return true; 4170 } 4171 4172 //___________________________________________________________________________________ 4173 bool Style::drawFrameWindowPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *) const 4174 { 4175 // copy rect and palette 4176 const auto &rect(option->rect); 4177 const auto &palette(option->palette); 4178 const State state(option->state); 4179 const bool selected(state & State_Selected); 4180 4181 // render frame outline 4182 const auto outline(_helper->frameOutlineColor(palette, false, selected)); 4183 _helper->renderMenuFrame(painter, rect, QColor(), outline); 4184 4185 return true; 4186 } 4187 4188 //___________________________________________________________________________________ 4189 bool Style::drawIndicatorArrowPrimitive(ArrowOrientation orientation, const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4190 { 4191 // store rect and palette 4192 const auto &rect(option->rect); 4193 const auto &palette(option->palette); 4194 4195 // store state 4196 const State &state(option->state); 4197 const bool enabled(state & State_Enabled); 4198 bool mouseOver(enabled && (state & State_MouseOver)); 4199 bool hasFocus(enabled && (state & State_HasFocus)); 4200 4201 // detect special buttons 4202 const bool inTabBar(widget && qobject_cast<const QTabBar *>(widget->parentWidget())); 4203 const bool inToolButton(qstyleoption_cast<const QStyleOptionToolButton *>(option)); 4204 4205 // color 4206 QColor color; 4207 if (inTabBar) { 4208 // for tabbar arrows one uses animations to get the arrow color 4209 /* 4210 * get animation state 4211 * there is no need to update the engine since this was already done when rendering the frame 4212 */ 4213 const AnimationMode mode(_animations->widgetStateEngine().buttonAnimationMode(widget)); 4214 const qreal opacity(_animations->widgetStateEngine().buttonOpacity(widget)); 4215 color = _helper->arrowColor(palette, mouseOver, hasFocus, opacity, mode); 4216 4217 } else if (mouseOver && !inToolButton) { 4218 color = _helper->hoverColor(palette); 4219 4220 } else if (inToolButton) { 4221 const bool flat(state & State_AutoRaise); 4222 4223 // cast option 4224 const QStyleOptionToolButton *toolButtonOption(static_cast<const QStyleOptionToolButton *>(option)); 4225 const auto menuStyle = BreezePrivate::toolButtonMenuArrowStyle(toolButtonOption); 4226 const bool sunken = state & State_Sunken; 4227 const bool checked = state & State_On; 4228 const bool arrowHover = mouseOver && (toolButtonOption->activeSubControls & SC_ToolButtonMenu); 4229 if (flat && menuStyle != BreezePrivate::ToolButtonMenuArrowStyle::None) { 4230 if (sunken && !mouseOver) { 4231 color = palette.color(QPalette::HighlightedText); 4232 } else if (checked && !mouseOver) { 4233 color = _helper->arrowColor(palette, QPalette::WindowText); 4234 } else if (checked && arrowHover) { 4235 // If the button is checked we have a focus color tinted background on hover 4236 color = palette.color(QPalette::HighlightedText); 4237 } else { 4238 // for menu arrows in flat toolbutton one uses animations to get the arrow color 4239 // handle arrow over animation 4240 _animations->toolButtonEngine().updateState(widget, AnimationHover, arrowHover); 4241 4242 const bool animated(_animations->toolButtonEngine().isAnimated(widget, AnimationHover)); 4243 const qreal opacity(_animations->toolButtonEngine().opacity(widget, AnimationHover)); 4244 4245 color = _helper->arrowColor(palette, arrowHover, false, opacity, animated ? AnimationHover : AnimationNone); 4246 } 4247 4248 } else if (flat) { 4249 if (sunken && hasFocus && !mouseOver) { 4250 color = palette.color(QPalette::WindowText); 4251 } else { 4252 color = _helper->arrowColor(palette, QPalette::WindowText); 4253 } 4254 4255 } else if (hasFocus && !mouseOver) { 4256 color = palette.color(QPalette::ButtonText); 4257 4258 } else { 4259 color = _helper->arrowColor(palette, QPalette::ButtonText); 4260 } 4261 4262 } else { 4263 color = _helper->arrowColor(palette, QPalette::WindowText); 4264 } 4265 4266 // render 4267 _helper->renderArrow(painter, rect, color, orientation); 4268 4269 return true; 4270 } 4271 4272 //___________________________________________________________________________________ 4273 bool Style::drawIndicatorHeaderArrowPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *) const 4274 { 4275 const auto headerOption(qstyleoption_cast<const QStyleOptionHeader *>(option)); 4276 const State &state(option->state); 4277 4278 // arrow orientation 4279 ArrowOrientation orientation(ArrowNone); 4280 if (state & State_UpArrow || (headerOption && headerOption->sortIndicator == QStyleOptionHeader::SortUp)) { 4281 orientation = ArrowUp; 4282 } else if (state & State_DownArrow || (headerOption && headerOption->sortIndicator == QStyleOptionHeader::SortDown)) { 4283 orientation = ArrowDown; 4284 } 4285 if (orientation == ArrowNone) { 4286 return true; 4287 } 4288 4289 // invert arrows if requested by (hidden) options 4290 if (StyleConfigData::viewInvertSortIndicator()) { 4291 orientation = (orientation == ArrowUp) ? ArrowDown : ArrowUp; 4292 } 4293 4294 // state, rect and palette 4295 const auto &rect(option->rect); 4296 const auto &palette(option->palette); 4297 4298 // define color and polygon for drawing arrow 4299 const auto color = _helper->arrowColor(palette, QPalette::ButtonText); 4300 4301 // render 4302 _helper->renderArrow(painter, rect, color, orientation); 4303 4304 return true; 4305 } 4306 4307 //______________________________________________________________ 4308 bool Style::drawPanelButtonCommandPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4309 { 4310 // button state 4311 bool enabled = option->state & QStyle::State_Enabled; 4312 bool activeFocus = option->state & QStyle::State_HasFocus; 4313 // Using `widget->focusProxy() == nullptr` to work around a possible Qt bug 4314 // where buttons that have a focusProxy still show focus. 4315 bool visualFocus = activeFocus && option->state & QStyle::State_KeyboardFocusChange && (widget == nullptr || widget->focusProxy() == nullptr); 4316 bool hovered = option->state & QStyle::State_MouseOver; 4317 bool down = option->state & QStyle::State_Sunken; 4318 bool checked = option->state & QStyle::State_On; 4319 bool flat = false; 4320 bool hasMenu = false; 4321 // Use to determine if this button is a default button. 4322 bool defaultButton = false; 4323 bool hasNeutralHighlight = hasHighlightNeutral(widget, option); 4324 4325 const auto buttonOption = qstyleoption_cast<const QStyleOptionButton *>(option); 4326 if (buttonOption) { 4327 flat = buttonOption->features & QStyleOptionButton::Flat; 4328 hasMenu = buttonOption->features & QStyleOptionButton::HasMenu; 4329 // If autoDefault is re-enabled by undoing a change to this file and 4330 // we decide that the default button highlight moving around outside 4331 // of the QDialogButtonBox is annoying, we could add 4332 // ` && widget->parentWidget()->inherits("QDialogButtonBox")`. 4333 // The downside would be that you can't see which button is the 4334 // default button when a QPushButton in a QDialog outside of a 4335 // QDialogButtonBox is focused. 4336 defaultButton = buttonOption->features & QStyleOptionButton::DefaultButton; 4337 } 4338 4339 // NOTE: Using focus animation for bg down because the pressed animation only works on press when enabled for buttons and not on release. 4340 _animations->widgetStateEngine().updateState(widget, AnimationFocus, down && enabled); 4341 // NOTE: Using hover animation for all pen animations to prevent flickering when closing the menu. 4342 _animations->widgetStateEngine().updateState(widget, AnimationHover, (hovered || visualFocus || down) && enabled); 4343 qreal bgAnimation = _animations->widgetStateEngine().opacity(widget, AnimationFocus); 4344 qreal penAnimation = _animations->widgetStateEngine().opacity(widget, AnimationHover); 4345 4346 QHash<QByteArray, bool> stateProperties; 4347 stateProperties["enabled"] = enabled; 4348 stateProperties["visualFocus"] = visualFocus; 4349 stateProperties["hovered"] = hovered; 4350 stateProperties["down"] = down; 4351 stateProperties["checked"] = checked; 4352 stateProperties["flat"] = flat; 4353 stateProperties["hasMenu"] = hasMenu; 4354 stateProperties["defaultButton"] = defaultButton; 4355 stateProperties["hasNeutralHighlight"] = hasNeutralHighlight; 4356 stateProperties["isActiveWindow"] = widget ? widget->isActiveWindow() : true; 4357 4358 _helper->renderButtonFrame(painter, option->rect, option->palette, stateProperties, bgAnimation, penAnimation); 4359 4360 return true; 4361 } 4362 4363 //______________________________________________________________ 4364 bool Style::drawPanelButtonToolPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4365 { 4366 // button state 4367 bool enabled = option->state & QStyle::State_Enabled; 4368 bool activeFocus = option->state & QStyle::State_HasFocus; 4369 bool visualFocus = activeFocus && option->state & QStyle::State_KeyboardFocusChange && (widget == nullptr || widget->focusProxy() == nullptr); 4370 bool hovered = option->state & QStyle::State_MouseOver; 4371 bool down = option->state & QStyle::State_Sunken; 4372 bool checked = option->state & QStyle::State_On; 4373 bool flat = option->state & QStyle::State_AutoRaise; 4374 bool hasNeutralHighlight = hasHighlightNeutral(widget, option); 4375 4376 // NOTE: Using focus animation for bg down because the pressed animation only works on press when enabled for buttons and not on release. 4377 _animations->widgetStateEngine().updateState(widget, AnimationFocus, down && enabled); 4378 // NOTE: Using hover animation for all pen animations to prevent flickering when closing the menu. 4379 _animations->widgetStateEngine().updateState(widget, AnimationHover, (hovered || visualFocus || down) && enabled); 4380 qreal bgAnimation = _animations->widgetStateEngine().opacity(widget, AnimationFocus); 4381 qreal penAnimation = _animations->widgetStateEngine().opacity(widget, AnimationHover); 4382 4383 QRect baseRect = option->rect; 4384 // adjust frame in case of menu 4385 const auto menuStyle = BreezePrivate::toolButtonMenuArrowStyle(option); 4386 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::SubControl) { 4387 // NOTE: working around weird issue with flat toolbuttons having unusually wide rects 4388 const auto clipRect = baseRect.adjusted(0, 0, flat ? -Metrics::ToolButton_InlineIndicatorWidth - Metrics::ToolButton_ItemSpacing * 2 : 0, 0); 4389 painter->setClipRect(visualRect(option, clipRect)); 4390 baseRect.adjust(0, 0, Metrics::Frame_FrameRadius + PenWidth::Shadow, 0); 4391 baseRect = visualRect(option, baseRect); 4392 } 4393 4394 QHash<QByteArray, bool> stateProperties; 4395 stateProperties["enabled"] = enabled; 4396 stateProperties["visualFocus"] = visualFocus; 4397 stateProperties["hovered"] = hovered; 4398 stateProperties["down"] = down; 4399 stateProperties["checked"] = checked; 4400 stateProperties["flat"] = flat; 4401 stateProperties["hasNeutralHighlight"] = hasNeutralHighlight; 4402 stateProperties["isActiveWindow"] = widget ? widget->isActiveWindow() : true; 4403 4404 _helper->renderButtonFrame(painter, baseRect, option->palette, stateProperties, bgAnimation, penAnimation); 4405 if (painter->hasClipping()) { 4406 painter->setClipping(false); 4407 } 4408 4409 return true; 4410 } 4411 4412 //______________________________________________________________ 4413 bool Style::drawTabBarPanelButtonToolPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4414 { 4415 // copy palette and rect 4416 auto rect(option->rect); 4417 4418 // static_cast is safe here since check was already performed in calling function 4419 const QTabBar *tabBar(static_cast<QTabBar *>(widget->parentWidget())); 4420 4421 // overlap. 4422 // subtract 1, because of the empty pixel left the tabwidget frame 4423 const int overlap(Metrics::TabBar_BaseOverlap - 1); 4424 4425 // adjust rect based on tabbar shape 4426 switch (tabBar->shape()) { 4427 case QTabBar::RoundedNorth: 4428 case QTabBar::TriangularNorth: 4429 rect.adjust(0, 0, 0, -overlap); 4430 break; 4431 4432 case QTabBar::RoundedSouth: 4433 case QTabBar::TriangularSouth: 4434 rect.adjust(0, overlap, 0, 0); 4435 break; 4436 4437 case QTabBar::RoundedWest: 4438 case QTabBar::TriangularWest: 4439 rect.adjust(0, 0, -overlap, 0); 4440 break; 4441 4442 case QTabBar::RoundedEast: 4443 case QTabBar::TriangularEast: 4444 rect.adjust(overlap, 0, 0, 0); 4445 break; 4446 4447 default: 4448 break; 4449 } 4450 4451 // get the relevant palette 4452 const QWidget *parent(tabBar->parentWidget()); 4453 if (qobject_cast<const QTabWidget *>(parent)) { 4454 parent = parent->parentWidget(); 4455 } 4456 const auto &palette(parent ? parent->palette() : QApplication::palette()); 4457 const auto color = hasAlteredBackground(parent) ? _helper->frameBackgroundColor(palette) : palette.color(QPalette::Window); 4458 4459 // render flat background 4460 painter->setPen(Qt::NoPen); 4461 painter->setBrush(color); 4462 painter->drawRect(rect); 4463 4464 return true; 4465 } 4466 4467 //___________________________________________________________________________________ 4468 bool Style::drawPanelScrollAreaCornerPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4469 { 4470 // make sure background role matches viewport 4471 const QAbstractScrollArea *scrollArea; 4472 if ((scrollArea = qobject_cast<const QAbstractScrollArea *>(widget)) && scrollArea->viewport()) { 4473 // need to adjust clipRect in order not to render outside of frame 4474 const int frameWidth(pixelMetric(PM_DefaultFrameWidth, nullptr, scrollArea)); 4475 painter->setClipRect(insideMargin(scrollArea->rect(), frameWidth)); 4476 painter->setBrush(scrollArea->viewport()->palette().color(scrollArea->viewport()->backgroundRole())); 4477 painter->setPen(Qt::NoPen); 4478 painter->drawRect(option->rect); 4479 return true; 4480 4481 } else { 4482 return false; 4483 } 4484 } 4485 4486 //___________________________________________________________________________________ 4487 bool Style::drawPanelMenuPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4488 { 4489 /* 4490 * do nothing if menu is embedded in another widget 4491 * this corresponds to having a transparent background 4492 */ 4493 if (widget && !widget->isWindow()) { 4494 return true; 4495 } 4496 4497 const auto &palette(option->palette); 4498 const bool hasAlpha(_helper->hasAlphaChannel(widget)); 4499 const auto seamlessEdges = _helper->menuSeamlessEdges(widget); 4500 auto background(_helper->frameBackgroundColor(palette)); 4501 auto outline(_helper->frameOutlineColor(palette)); 4502 4503 painter->save(); 4504 4505 if (StyleConfigData::menuOpacity() < 100) { 4506 if (widget && widget->isWindow()) { 4507 painter->setCompositionMode(QPainter::CompositionMode_Source); 4508 } 4509 background.setAlphaF(StyleConfigData::menuOpacity() / 100.0); 4510 outline = _helper->alphaColor(palette.color(QPalette::WindowText), 0.25); 4511 } 4512 4513 _helper->renderMenuFrame(painter, option->rect, background, outline, hasAlpha, seamlessEdges); 4514 4515 painter->restore(); 4516 4517 return true; 4518 } 4519 4520 //___________________________________________________________________________________ 4521 bool Style::drawPanelTipLabelPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4522 { 4523 // force registration of widget 4524 if (widget && widget->window()) { 4525 _shadowHelper->registerWidget(widget->window(), true); 4526 } 4527 4528 const auto &palette(option->palette); 4529 const auto &background = palette.color(QPalette::ToolTipBase); 4530 const auto outline(KColorUtils::mix(palette.color(QPalette::ToolTipBase), palette.color(QPalette::ToolTipText), 0.25)); 4531 const bool hasAlpha(_helper->hasAlphaChannel(widget)); 4532 4533 _helper->renderMenuFrame(painter, option->rect, background, outline, hasAlpha); 4534 return true; 4535 } 4536 4537 //___________________________________________________________________________________ 4538 bool Style::drawPanelItemViewItemPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4539 { 4540 // cast option and check 4541 const auto viewItemOption = qstyleoption_cast<const QStyleOptionViewItem *>(option); 4542 if (!viewItemOption) { 4543 return false; 4544 } 4545 4546 // try cast widget 4547 const auto abstractItemView = qobject_cast<const QAbstractItemView *>(widget); 4548 4549 // store palette and rect 4550 const auto &palette(option->palette); 4551 auto rect(option->rect); 4552 4553 // store flags 4554 const State &state(option->state); 4555 const bool mouseOver((state & State_MouseOver) && (!abstractItemView || abstractItemView->selectionMode() != QAbstractItemView::NoSelection)); 4556 const bool selected(state & State_Selected); 4557 const bool enabled(state & State_Enabled); 4558 const bool active(state & State_Active); 4559 4560 const bool hasCustomBackground = viewItemOption->backgroundBrush.style() != Qt::NoBrush && !(state & State_Selected); 4561 const bool hasSolidBackground = !hasCustomBackground || viewItemOption->backgroundBrush.style() == Qt::SolidPattern; 4562 const bool hasAlternateBackground(viewItemOption->features & QStyleOptionViewItem::Alternate); 4563 4564 // do nothing if no background is to be rendered 4565 if (!(mouseOver || selected || hasCustomBackground || hasAlternateBackground)) { 4566 return true; 4567 } 4568 4569 // define color group 4570 QPalette::ColorGroup colorGroup; 4571 if (enabled) { 4572 colorGroup = active ? QPalette::Active : QPalette::Inactive; 4573 } else { 4574 colorGroup = QPalette::Disabled; 4575 } 4576 4577 // render alternate background 4578 if (hasAlternateBackground) { 4579 painter->setPen(Qt::NoPen); 4580 painter->setBrush(palette.brush(colorGroup, QPalette::AlternateBase)); 4581 painter->drawRect(rect); 4582 } 4583 4584 // stop here if no highlight is needed 4585 if (!(mouseOver || selected || hasCustomBackground)) { 4586 return true; 4587 } 4588 4589 // render custom background 4590 if (hasCustomBackground && !hasSolidBackground) { 4591 painter->setBrushOrigin(viewItemOption->rect.topLeft()); 4592 painter->setBrush(viewItemOption->backgroundBrush); 4593 painter->setPen(Qt::NoPen); 4594 painter->drawRect(viewItemOption->rect); 4595 return true; 4596 } 4597 4598 // render selection 4599 // define color 4600 QColor color; 4601 if (hasCustomBackground && hasSolidBackground) { 4602 color = viewItemOption->backgroundBrush.color(); 4603 } else { 4604 color = palette.color(colorGroup, QPalette::Highlight); 4605 } 4606 4607 // change color to implement mouse over 4608 if (mouseOver && !hasCustomBackground) { 4609 if (!selected) { 4610 color.setAlphaF(0.2); 4611 } else { 4612 color = color.lighter(110); 4613 } 4614 } 4615 4616 // render 4617 _helper->renderSelection(painter, rect, color); 4618 4619 return true; 4620 } 4621 4622 //___________________________________________________________________________________ 4623 bool Style::drawIndicatorCheckBoxPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4624 { 4625 // copy rect and palette 4626 const auto &rect(option->rect); 4627 const auto &palette(option->palette); 4628 4629 // store flags 4630 const State &state(option->state); 4631 const bool enabled(state & State_Enabled); 4632 const bool sunken(state & State_Sunken); 4633 const bool mouseOver(enabled && (state & State_MouseOver)); 4634 4635 // checkbox state 4636 CheckBoxState checkBoxState(CheckOff); 4637 if (state & State_NoChange) { 4638 checkBoxState = CheckPartial; 4639 } else if (state & State_On) { 4640 checkBoxState = CheckOn; 4641 } 4642 4643 // animation state 4644 _animations->widgetStateEngine().updateState(widget, AnimationHover, mouseOver); 4645 _animations->widgetStateEngine().updateState(widget, AnimationPressed, checkBoxState != CheckOff); 4646 auto target = checkBoxState; 4647 if (_animations->widgetStateEngine().isAnimated(widget, AnimationPressed)) { 4648 checkBoxState = CheckAnimated; 4649 } 4650 const qreal animation(_animations->widgetStateEngine().opacity(widget, AnimationPressed)); 4651 4652 const qreal opacity(_animations->widgetStateEngine().opacity(widget, AnimationHover)); 4653 4654 // render 4655 _helper->renderCheckBoxBackground(painter, rect, palette, checkBoxState, hasHighlightNeutral(widget, option, mouseOver), sunken, animation); 4656 _helper 4657 ->renderCheckBox(painter, rect, palette, mouseOver, checkBoxState, target, hasHighlightNeutral(widget, option, mouseOver), sunken, animation, opacity); 4658 return true; 4659 } 4660 4661 //___________________________________________________________________________________ 4662 bool Style::drawIndicatorRadioButtonPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4663 { 4664 // copy rect and palette 4665 const auto &rect(option->rect); 4666 const auto &palette(option->palette); 4667 4668 // store flags 4669 const State &state(option->state); 4670 const bool enabled(state & State_Enabled); 4671 const bool sunken(state & State_Sunken); 4672 const bool mouseOver(enabled && (state & State_MouseOver)); 4673 4674 // radio button state 4675 RadioButtonState radioButtonState((state & State_On) ? RadioOn : RadioOff); 4676 4677 // animation state 4678 _animations->widgetStateEngine().updateState(widget, AnimationHover, mouseOver); 4679 _animations->widgetStateEngine().updateState(widget, AnimationPressed, radioButtonState != RadioOff); 4680 if (_animations->widgetStateEngine().isAnimated(widget, AnimationPressed)) { 4681 radioButtonState = RadioAnimated; 4682 } 4683 const qreal animation(_animations->widgetStateEngine().opacity(widget, AnimationPressed)); 4684 4685 // colors 4686 const qreal opacity(_animations->widgetStateEngine().opacity(widget, AnimationHover)); 4687 4688 // render 4689 _helper->renderRadioButtonBackground(painter, rect, palette, radioButtonState, hasHighlightNeutral(widget, option, mouseOver), sunken, animation); 4690 _helper->renderRadioButton(painter, rect, palette, mouseOver, radioButtonState, hasHighlightNeutral(widget, option, mouseOver), sunken, animation, opacity); 4691 4692 return true; 4693 } 4694 4695 //___________________________________________________________________________________ 4696 bool Style::drawIndicatorButtonDropDownPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4697 { 4698 // cast option and check 4699 const auto complexOption(qstyleoption_cast<const QStyleOptionComplex *>(option)); 4700 if (!complexOption || !(complexOption->subControls & SC_ToolButtonMenu)) { 4701 return true; 4702 } 4703 4704 // button state 4705 bool enabled = option->state & QStyle::State_Enabled; 4706 bool activeFocus = option->state & QStyle::State_HasFocus; 4707 bool visualFocus = activeFocus && option->state & QStyle::State_KeyboardFocusChange && (widget == nullptr || widget->focusProxy() == nullptr); 4708 bool hovered = option->state & QStyle::State_MouseOver; 4709 bool down = option->state & QStyle::State_Sunken; 4710 bool checked = option->state & QStyle::State_On; 4711 bool flat = option->state & QStyle::State_AutoRaise; 4712 bool hasNeutralHighlight = hasHighlightNeutral(widget, option); 4713 4714 // update animation state 4715 // NOTE: Using focus animation for bg down because the pressed animation only works on press when enabled for buttons and not on release. 4716 _animations->widgetStateEngine().updateState(widget, AnimationFocus, down && enabled); 4717 // NOTE: Using hover animation for all pen animations to prevent flickering when closing the menu. 4718 _animations->widgetStateEngine().updateState(widget, AnimationHover, (hovered || visualFocus || down) && enabled); 4719 qreal bgAnimation = _animations->widgetStateEngine().opacity(widget, AnimationFocus); 4720 qreal penAnimation = _animations->widgetStateEngine().opacity(widget, AnimationHover); 4721 4722 QRect baseRect = option->rect; 4723 const auto clipRect = visualRect(option, baseRect); 4724 painter->setClipRect(clipRect); 4725 baseRect.adjust(-Metrics::Frame_FrameRadius - qRound(PenWidth::Shadow), 0, 0, 0); 4726 baseRect = visualRect(option, baseRect); 4727 4728 QHash<QByteArray, bool> stateProperties; 4729 stateProperties["enabled"] = enabled; 4730 stateProperties["visualFocus"] = visualFocus; 4731 stateProperties["hovered"] = hovered; 4732 stateProperties["down"] = down; 4733 stateProperties["checked"] = checked; 4734 stateProperties["flat"] = flat; 4735 stateProperties["hasNeutralHighlight"] = hasNeutralHighlight; 4736 stateProperties["isActiveWindow"] = widget ? widget->isActiveWindow() : true; 4737 4738 _helper->renderButtonFrame(painter, baseRect, option->palette, stateProperties, bgAnimation, penAnimation); 4739 4740 QRectF frameRect = _helper->strokedRect(_helper->shadowedRect(baseRect)); 4741 4742 // also render separator 4743 if (!flat || activeFocus || hovered || down || checked || penAnimation != AnimationData::OpacityInvalid) { 4744 painter->setBrush(Qt::NoBrush); 4745 if (option->direction == Qt::RightToLeft) { 4746 QRectF separatorRect = frameRect.adjusted(0, 0, -Metrics::Frame_FrameRadius - PenWidth::Shadow, 0); 4747 painter->drawLine(separatorRect.topRight(), separatorRect.bottomRight()); 4748 } else { 4749 QRectF separatorRect = frameRect.adjusted(Metrics::Frame_FrameRadius + PenWidth::Shadow, 0, 0, 0); 4750 painter->drawLine(separatorRect.topLeft(), separatorRect.bottomLeft()); 4751 } 4752 } 4753 4754 if (painter->hasClipping()) { 4755 painter->setClipping(false); 4756 } 4757 4758 return true; 4759 } 4760 4761 //___________________________________________________________________________________ 4762 bool Style::drawIndicatorTabClosePrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4763 { 4764 // get icon and check 4765 QIcon icon(standardIcon(SP_TitleBarCloseButton, option, widget)); 4766 if (icon.isNull()) { 4767 return false; 4768 } 4769 4770 // store state 4771 const State &state(option->state); 4772 const bool enabled(state & State_Enabled); 4773 const bool active(state & State_Raised); 4774 const bool sunken(state & State_Sunken); 4775 4776 // decide icon mode and state 4777 QIcon::Mode iconMode; 4778 QIcon::State iconState; 4779 if (!enabled) { 4780 iconMode = QIcon::Disabled; 4781 iconState = QIcon::Off; 4782 4783 } else { 4784 if (active) { 4785 iconMode = QIcon::Active; 4786 } else { 4787 iconMode = QIcon::Normal; 4788 } 4789 4790 iconState = sunken ? QIcon::On : QIcon::Off; 4791 } 4792 4793 // icon size 4794 const int iconWidth(pixelMetric(QStyle::PM_SmallIconSize, option, widget)); 4795 const QSize iconSize(iconWidth, iconWidth); 4796 4797 // get pixmap 4798 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 4799 const QPixmap pixmap(_helper->coloredIcon(icon, option->palette, iconSize, dpr, iconMode, iconState)); 4800 4801 // render 4802 drawItemPixmap(painter, option->rect, Qt::AlignCenter, pixmap); 4803 return true; 4804 } 4805 4806 //___________________________________________________________________________________ 4807 bool Style::drawIndicatorTabTearPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *) const 4808 { 4809 // cast option and check 4810 const auto tabOption(qstyleoption_cast<const QStyleOptionTab *>(option)); 4811 if (!tabOption) { 4812 return true; 4813 } 4814 4815 // store palette and rect 4816 const auto &palette(option->palette); 4817 auto rect(option->rect); 4818 4819 const bool reverseLayout(option->direction == Qt::RightToLeft); 4820 4821 const auto color(_helper->alphaColor(palette.color(QPalette::WindowText), 0.2)); 4822 painter->setRenderHint(QPainter::Antialiasing, false); 4823 painter->setPen(color); 4824 painter->setBrush(Qt::NoBrush); 4825 switch (tabOption->shape) { 4826 case QTabBar::TriangularNorth: 4827 case QTabBar::RoundedNorth: 4828 rect.adjust(0, 1, 0, 0); 4829 if (reverseLayout) { 4830 painter->drawLine(rect.topRight(), rect.bottomRight()); 4831 } else { 4832 painter->drawLine(rect.topLeft(), rect.bottomLeft()); 4833 } 4834 break; 4835 4836 case QTabBar::TriangularSouth: 4837 case QTabBar::RoundedSouth: 4838 rect.adjust(0, 0, 0, -1); 4839 if (reverseLayout) { 4840 painter->drawLine(rect.topRight(), rect.bottomRight()); 4841 } else { 4842 painter->drawLine(rect.topLeft(), rect.bottomLeft()); 4843 } 4844 break; 4845 4846 case QTabBar::TriangularWest: 4847 case QTabBar::RoundedWest: 4848 rect.adjust(1, 0, 0, 0); 4849 painter->drawLine(rect.topLeft(), rect.topRight()); 4850 break; 4851 4852 case QTabBar::TriangularEast: 4853 case QTabBar::RoundedEast: 4854 rect.adjust(0, 0, -1, 0); 4855 painter->drawLine(rect.topLeft(), rect.topRight()); 4856 break; 4857 4858 default: 4859 break; 4860 } 4861 4862 return true; 4863 } 4864 4865 //___________________________________________________________________________________ 4866 bool Style::drawIndicatorToolBarHandlePrimitive(const QStyleOption *option, QPainter *painter, const QWidget *) const 4867 { 4868 // do nothing if disabled from options 4869 if (!StyleConfigData::toolBarDrawItemSeparator()) { 4870 return true; 4871 } 4872 4873 // store rect and palette 4874 auto rect(option->rect); 4875 const auto &palette(option->palette); 4876 4877 // store state 4878 const State &state(option->state); 4879 const bool separatorIsVertical(state & State_Horizontal); 4880 4881 // define color and render 4882 const auto color(_helper->separatorColor(palette)); 4883 if (separatorIsVertical) { 4884 rect.setWidth(Metrics::ToolBar_HandleWidth); 4885 rect = centerRect(option->rect, rect.size()); 4886 rect.setWidth(3); 4887 _helper->renderSeparator(painter, rect, color, separatorIsVertical); 4888 4889 rect.translate(2, 0); 4890 _helper->renderSeparator(painter, rect, color, separatorIsVertical); 4891 4892 } else { 4893 rect.setHeight(Metrics::ToolBar_HandleWidth); 4894 rect = centerRect(option->rect, rect.size()); 4895 rect.setHeight(3); 4896 _helper->renderSeparator(painter, rect, color, separatorIsVertical); 4897 4898 rect.translate(0, 2); 4899 _helper->renderSeparator(painter, rect, color, separatorIsVertical); 4900 } 4901 4902 return true; 4903 } 4904 4905 //___________________________________________________________________________________ 4906 bool Style::drawIndicatorToolBarSeparatorPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 4907 { 4908 /* 4909 * do nothing if disabled from options 4910 * also need to check if widget is a combobox, because of Qt hack using 'toolbar' separator primitive 4911 * for rendering separators in comboboxes 4912 */ 4913 if (!(StyleConfigData::toolBarDrawItemSeparator() || qobject_cast<const QComboBox *>(widget))) { 4914 return true; 4915 } 4916 4917 // store rect and palette 4918 const auto &rect(option->rect); 4919 const auto &palette(option->palette); 4920 4921 // store state 4922 const State &state(option->state); 4923 const bool separatorIsVertical(state & State_Horizontal); 4924 4925 // define color and render 4926 const auto color(_helper->separatorColor(palette)); 4927 _helper->renderSeparator(painter, rect, color, separatorIsVertical); 4928 4929 return true; 4930 } 4931 4932 //___________________________________________________________________________________ 4933 bool Style::drawIndicatorBranchPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *) const 4934 { 4935 // copy rect and palette 4936 const auto &rect(option->rect); 4937 const auto &palette(option->palette); 4938 4939 // state 4940 const State &state(option->state); 4941 const bool reverseLayout(option->direction == Qt::RightToLeft); 4942 4943 // draw expander 4944 int expanderAdjust = 0; 4945 if (state & State_Children) { 4946 // state 4947 const bool expanderOpen(state & State_Open); 4948 const bool enabled(state & State_Enabled); 4949 const bool mouseOver(enabled && (state & State_MouseOver)); 4950 4951 // expander rect 4952 int expanderSize = qMin(rect.width(), rect.height()); 4953 expanderSize = qMin(expanderSize, int(Metrics::ItemView_ArrowSize)); 4954 expanderAdjust = expanderSize / 2 + 1; 4955 const auto arrowRect = centerRect(rect, expanderSize, expanderSize); 4956 4957 // get orientation from option 4958 ArrowOrientation orientation; 4959 if (expanderOpen) { 4960 orientation = ArrowDown; 4961 } else if (reverseLayout) { 4962 orientation = ArrowLeft; 4963 } else { 4964 orientation = ArrowRight; 4965 } 4966 4967 // color 4968 const auto arrowColor(mouseOver ? _helper->hoverColor(palette) : _helper->arrowColor(palette, QPalette::Text)); 4969 4970 // render 4971 _helper->renderArrow(painter, arrowRect, arrowColor, orientation); 4972 } 4973 4974 // tree branches 4975 if (!StyleConfigData::viewDrawTreeBranchLines()) { 4976 return true; 4977 } 4978 4979 const auto center(rect.center()); 4980 const auto lineColor(KColorUtils::mix(palette.color(QPalette::Base), palette.color(QPalette::Text), 0.25)); 4981 painter->setRenderHint(QPainter::Antialiasing, true); 4982 painter->translate(0.5, 0.5); 4983 painter->setPen(QPen(lineColor, 1)); 4984 if (state & (State_Item | State_Children | State_Sibling)) { 4985 const QLineF line(QPointF(center.x(), rect.top()), QPointF(center.x(), center.y() - expanderAdjust - 1)); 4986 painter->drawLine(line); 4987 } 4988 4989 // The right/left (depending on direction) line gets drawn if we have an item 4990 if (state & State_Item) { 4991 const QLineF line = reverseLayout ? QLineF(QPointF(rect.left(), center.y()), QPointF(center.x() - expanderAdjust, center.y())) 4992 : QLineF(QPointF(center.x() + expanderAdjust, center.y()), QPointF(rect.right(), center.y())); 4993 painter->drawLine(line); 4994 } 4995 4996 // The bottom if we have a sibling 4997 if (state & State_Sibling) { 4998 const QLineF line(QPointF(center.x(), center.y() + expanderAdjust), QPointF(center.x(), rect.bottom())); 4999 painter->drawLine(line); 5000 } 5001 5002 return true; 5003 } 5004 5005 bool Style::drawDockWidgetResizeHandlePrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5006 { 5007 Q_UNUSED(widget); 5008 5009 painter->setBrush(_helper->separatorColor(option->palette)); 5010 painter->setPen(Qt::NoPen); 5011 painter->drawRect(option->rect); 5012 5013 return true; 5014 } 5015 5016 bool Style::drawPanelStatusBarPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5017 { 5018 if (widget && !widget->property("_breeze_statusbar_separator").toBool() && widget->parent() && !widget->parent()->inherits("QMainWindow")) { 5019 return true; 5020 } 5021 auto rect(option->rect); 5022 const auto color(_helper->separatorColor(option->palette)); 5023 const auto size = pixelMetric(QStyle::PM_SplitterWidth, option, widget); 5024 rect.setHeight(size); 5025 _helper->renderSeparator(painter, rect, color, false); 5026 5027 return true; 5028 } 5029 5030 //___________________________________________________________________________________ 5031 bool Style::drawPushButtonLabelControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5032 { 5033 // cast option and check 5034 const auto buttonOption(qstyleoption_cast<const QStyleOptionButton *>(option)); 5035 if (!buttonOption) { 5036 return true; 5037 } 5038 5039 // copy rect and palette 5040 const auto &rect(option->rect); 5041 const auto &palette(option->palette); 5042 5043 // state 5044 const State &state(option->state); 5045 const bool enabled(state & State_Enabled); 5046 const bool sunken(state & (State_On | State_Sunken)); 5047 const bool mouseOver(enabled && (option->state & State_MouseOver)); 5048 const bool hasFocus(enabled && !mouseOver && (option->state & State_HasFocus)); 5049 const bool flat(buttonOption->features & QStyleOptionButton::Flat); 5050 5051 // content 5052 const bool hasText(!buttonOption->text.isEmpty()); 5053 const bool hasIcon((showIconsOnPushButtons() || flat || !hasText) && !buttonOption->icon.isNull()); 5054 5055 // contents 5056 auto contentsRect(rect); 5057 5058 // color role 5059 QPalette::ColorRole textRole; 5060 if (flat) { 5061 if (hasFocus && sunken) { 5062 textRole = QPalette::WindowText; 5063 } else { 5064 textRole = QPalette::WindowText; 5065 } 5066 5067 } else if (hasFocus) { 5068 textRole = QPalette::ButtonText; 5069 } else { 5070 textRole = QPalette::ButtonText; 5071 } 5072 5073 // menu arrow 5074 if (buttonOption->features & QStyleOptionButton::HasMenu) { 5075 // define rect 5076 auto arrowRect(contentsRect); 5077 arrowRect.setLeft(contentsRect.right() - Metrics::MenuButton_IndicatorWidth + 1); 5078 arrowRect = centerRect(arrowRect, Metrics::MenuButton_IndicatorWidth, Metrics::MenuButton_IndicatorWidth); 5079 5080 contentsRect.setRight(arrowRect.left() - Metrics::Button_ItemSpacing - 1); 5081 contentsRect.adjust(Metrics::Button_MarginWidth, 0, 0, 0); 5082 5083 arrowRect = visualRect(option, arrowRect); 5084 5085 // define color 5086 const auto arrowColor(_helper->arrowColor(palette, textRole)); 5087 _helper->renderArrow(painter, arrowRect, arrowColor, ArrowDown); 5088 } 5089 5090 // icon size 5091 QSize iconSize; 5092 if (hasIcon) { 5093 iconSize = buttonOption->iconSize; 5094 if (!iconSize.isValid()) { 5095 const int metric(pixelMetric(PM_SmallIconSize, option, widget)); 5096 iconSize = QSize(metric, metric); 5097 } 5098 } 5099 5100 // text size 5101 const int textFlags(_mnemonics->textFlags() | Qt::AlignCenter); 5102 const QSize textSize(option->fontMetrics.size(textFlags, buttonOption->text)); 5103 5104 // adjust text and icon rect based on options 5105 QRect iconRect; 5106 QRect textRect; 5107 5108 if (hasText && !hasIcon) { 5109 textRect = contentsRect; 5110 } else if (hasIcon && !hasText) { 5111 iconRect = contentsRect; 5112 } else { 5113 const int contentsWidth(iconSize.width() + textSize.width() + Metrics::Button_ItemSpacing); 5114 iconRect = QRect( 5115 QPoint(contentsRect.left() + (contentsRect.width() - contentsWidth) / 2, contentsRect.top() + (contentsRect.height() - iconSize.height()) / 2), 5116 iconSize); 5117 textRect = QRect(QPoint(iconRect.right() + Metrics::ToolButton_ItemSpacing + 1, contentsRect.top() + (contentsRect.height() - textSize.height()) / 2), 5118 textSize); 5119 } 5120 5121 // handle right to left 5122 if (iconRect.isValid()) { 5123 iconRect = visualRect(option, iconRect); 5124 } 5125 if (textRect.isValid()) { 5126 textRect = visualRect(option, textRect); 5127 } 5128 5129 // make sure there is enough room for icon 5130 if (iconRect.isValid()) { 5131 iconRect = centerRect(iconRect, iconSize); 5132 } 5133 5134 // render icon 5135 if (hasIcon && iconRect.isValid()) { 5136 // icon state and mode 5137 const QIcon::State iconState(sunken ? QIcon::On : QIcon::Off); 5138 QIcon::Mode iconMode; 5139 if (!enabled) { 5140 iconMode = QIcon::Disabled; 5141 } else { 5142 iconMode = QIcon::Normal; 5143 } 5144 5145 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 5146 const auto pixmap = _helper->coloredIcon(buttonOption->icon, buttonOption->palette, iconSize, dpr, iconMode, iconState); 5147 drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); 5148 } 5149 5150 // render text 5151 if (hasText && textRect.isValid()) { 5152 drawItemText(painter, textRect, textFlags, palette, enabled, buttonOption->text, textRole); 5153 } 5154 5155 return true; 5156 } 5157 5158 //___________________________________________________________________________________ 5159 bool Style::drawToolButtonLabelControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5160 { 5161 // cast option and check 5162 const auto toolButtonOption(qstyleoption_cast<const QStyleOptionToolButton *>(option)); 5163 5164 // copy rect and palette 5165 const auto &rect = option->rect; 5166 5167 // state 5168 const State &state(option->state); 5169 const bool enabled(state & State_Enabled); 5170 const bool sunken(state & (State_On | State_Sunken)); 5171 const bool mouseOver(enabled && (option->state & State_MouseOver)); 5172 const bool flat(state & State_AutoRaise); 5173 5174 // focus flag is set to match the background color in either renderButtonFrame or renderToolButtonFrame 5175 bool hasFocus(false); 5176 if (flat) { 5177 hasFocus = enabled && !mouseOver && (option->state & State_HasFocus); 5178 } else { 5179 hasFocus = enabled && !mouseOver && (option->state & (State_HasFocus | State_Sunken)); 5180 } 5181 5182 // contents 5183 auto contentsRect(rect); 5184 5185 const auto menuStyle = BreezePrivate::toolButtonMenuArrowStyle(option); 5186 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::InlineLarge) { 5187 // Place contents to the left of the menu arrow. 5188 const auto arrowRect = toolButtonSubControlRect(toolButtonOption, SC_ToolButtonMenu, widget); 5189 contentsRect.setRight(contentsRect.right() - arrowRect.width()); 5190 } 5191 5192 const auto toolButtonStyle = toolButtonOption->toolButtonStyle; 5193 const bool hasArrow = toolButtonOption->features & QStyleOptionToolButton::Arrow; 5194 bool hasIcon = toolButtonStyle != Qt::ToolButtonTextOnly 5195 && ((!toolButtonOption->icon.isNull() && !toolButtonOption->iconSize.isEmpty()) 5196 || hasArrow); 5197 bool hasText = toolButtonStyle != Qt::ToolButtonIconOnly && !toolButtonOption->text.isEmpty(); 5198 const bool textUnderIcon = hasIcon && hasText && toolButtonStyle == Qt::ToolButtonTextUnderIcon; 5199 5200 const QSize &availableSize = contentsRect.size(); 5201 const QSize &iconSize = toolButtonOption->iconSize; 5202 int textFlags(_mnemonics->textFlags()); 5203 const QSize textSize(option->fontMetrics.size(textFlags, toolButtonOption->text)); 5204 5205 // adjust text and icon rect based on options 5206 QRect iconRect; 5207 QRect textRect; 5208 5209 if (availableSize.isEmpty()) { 5210 hasIcon = false; 5211 hasText = false; 5212 } 5213 5214 if (hasIcon && !hasText) { // icon only 5215 iconRect = contentsRect; 5216 } else if (!hasIcon && hasText) { // text only 5217 textRect = visualRect(option, contentsRect); 5218 textFlags |= Qt::AlignCenter; 5219 } else if (textUnderIcon) { 5220 const int contentsHeight(iconSize.height() + textSize.height() + Metrics::ToolButton_ItemSpacing); 5221 iconRect = { 5222 QPoint(contentsRect.left() + (contentsRect.width() - iconSize.width()) / 2, 5223 contentsRect.top() + (contentsRect.height() - contentsHeight) / 2), 5224 iconSize 5225 }; 5226 textRect = { 5227 QPoint(contentsRect.left() + (contentsRect.width() - textSize.width()) / 2, 5228 iconRect.bottom() + Metrics::ToolButton_ItemSpacing + 1), 5229 textSize 5230 }; 5231 5232 // handle right to left layouts 5233 iconRect = visualRect(option, iconRect); 5234 textRect = visualRect(option, textRect); 5235 5236 textFlags |= Qt::AlignCenter; 5237 } else if (hasIcon && hasText) { 5238 const bool leftAlign(widget && widget->property(PropertyNames::toolButtonAlignment).toInt() == Qt::AlignLeft); 5239 if (leftAlign) { 5240 const int marginWidth(Metrics::Button_MarginWidth + Metrics::Frame_FrameWidth + 1); 5241 iconRect = { 5242 QPoint(contentsRect.left() + marginWidth, 5243 contentsRect.top() + (contentsRect.height() - iconSize.height()) / 2), 5244 iconSize 5245 }; 5246 } else { 5247 const int contentsWidth(iconSize.width() + textSize.width() + Metrics::ToolButton_ItemSpacing); 5248 iconRect = { 5249 QPoint(contentsRect.left() + (contentsRect.width() - contentsWidth) / 2, 5250 contentsRect.top() + (contentsRect.height() - iconSize.height()) / 2), 5251 iconSize 5252 }; 5253 } 5254 5255 const int padding = (contentsRect.height() - textSize.height()) / 2; 5256 textRect = {QPoint(iconRect.right() + Metrics::ToolButton_ItemSpacing + 1, contentsRect.top() + padding), 5257 QSize(textSize.width(), contentsRect.height() - 2 * padding)}; 5258 5259 // Don't use QRect::isEmpty(). It does not return true if size is 0x0. 5260 hasText = !textRect.size().isEmpty(); 5261 5262 // handle right to left layouts 5263 iconRect = visualRect(option, iconRect); 5264 textRect = visualRect(option, textRect); 5265 5266 textFlags |= Qt::AlignLeft | Qt::AlignVCenter; 5267 } 5268 5269 // render arrow or icon 5270 if (hasIcon) { 5271 iconRect = centerRect(iconRect, iconSize); 5272 if (hasArrow) { 5273 QStyleOptionToolButton copy(*toolButtonOption); 5274 copy.rect = iconRect; 5275 switch (toolButtonOption->arrowType) { 5276 case Qt::LeftArrow: 5277 drawPrimitive(PE_IndicatorArrowLeft, ©, painter, widget); 5278 break; 5279 case Qt::RightArrow: 5280 drawPrimitive(PE_IndicatorArrowRight, ©, painter, widget); 5281 break; 5282 case Qt::UpArrow: 5283 drawPrimitive(PE_IndicatorArrowUp, ©, painter, widget); 5284 break; 5285 case Qt::DownArrow: 5286 drawPrimitive(PE_IndicatorArrowDown, ©, painter, widget); 5287 break; 5288 default: 5289 break; 5290 } 5291 5292 } else { 5293 // icon state and mode 5294 const QIcon::State iconState(sunken ? QIcon::On : QIcon::Off); 5295 QIcon::Mode iconMode; 5296 if (!enabled) { 5297 iconMode = QIcon::Disabled; 5298 } else if (!flat && hasFocus) { 5299 iconMode = QIcon::Selected; 5300 } else if (mouseOver && flat) { 5301 iconMode = QIcon::Active; 5302 } else { 5303 iconMode = QIcon::Normal; 5304 } 5305 5306 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 5307 const QPixmap pixmap = _helper->coloredIcon(toolButtonOption->icon, toolButtonOption->palette, iconSize, dpr, iconMode, iconState); 5308 drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); 5309 } 5310 } 5311 5312 // render text 5313 if (hasText) { 5314 QPalette::ColorRole textRole(QPalette::ButtonText); 5315 if (flat) { 5316 textRole = QPalette::WindowText; 5317 } 5318 5319 auto palette = option->palette; 5320 5321 painter->setFont(toolButtonOption->font); 5322 drawItemText(painter, textRect, textFlags, palette, enabled, toolButtonOption->text, textRole); 5323 } 5324 5325 return true; 5326 } 5327 5328 //___________________________________________________________________________________ 5329 bool Style::drawCheckBoxLabelControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5330 { 5331 // cast option and check 5332 const auto buttonOption(qstyleoption_cast<const QStyleOptionButton *>(option)); 5333 if (!buttonOption) { 5334 return true; 5335 } 5336 5337 // copy palette and rect 5338 const auto &palette(option->palette); 5339 const auto &rect(option->rect); 5340 5341 // store state 5342 const State &state(option->state); 5343 const bool enabled(state & State_Enabled); 5344 5345 // text alignment 5346 const bool reverseLayout(option->direction == Qt::RightToLeft); 5347 const Qt::Alignment iconFlags(Qt::AlignVCenter | Qt::AlignLeft); 5348 const Qt::Alignment textFlags(_mnemonics->textFlags() | Qt::AlignVCenter | (reverseLayout ? Qt::AlignRight : Qt::AlignLeft)); 5349 5350 // text rect 5351 auto textRect(rect); 5352 // focus rect 5353 auto focusRect(rect); 5354 5355 // render icon 5356 if (!buttonOption->icon.isNull()) { 5357 const QIcon::Mode mode(enabled ? QIcon::Normal : QIcon::Disabled); 5358 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 5359 const QPixmap pixmap(_helper->coloredIcon(buttonOption->icon, buttonOption->palette, buttonOption->iconSize, dpr, mode)); 5360 drawItemPixmap(painter, rect, iconFlags, pixmap); 5361 5362 // adjust rect (copied from QCommonStyle) 5363 textRect.setLeft(textRect.left() + buttonOption->iconSize.width() + 4); 5364 textRect = visualRect(option, textRect); 5365 // shrink, flip and vertically center 5366 focusRect.setWidth(buttonOption->iconSize.width()); 5367 focusRect = visualRect(option, focusRect); 5368 focusRect = centerRect(focusRect, buttonOption->iconSize); 5369 } 5370 5371 // render text 5372 if (!buttonOption->text.isEmpty()) { 5373 textRect = option->fontMetrics.boundingRect(textRect, textFlags, buttonOption->text); 5374 focusRect.setTop(textRect.top()); 5375 focusRect.setBottom(textRect.bottom()); 5376 if (reverseLayout) { 5377 focusRect.setLeft(textRect.left()); 5378 } else { 5379 focusRect.setRight(textRect.right()); 5380 } 5381 drawItemText(painter, textRect, textFlags, palette, enabled, buttonOption->text, QPalette::WindowText); 5382 } 5383 5384 // check focus state 5385 const bool hasFocus(enabled && (state & State_HasFocus)); 5386 5387 // update animation state 5388 _animations->widgetStateEngine().updateState(widget, AnimationFocus, hasFocus); 5389 5390 const bool isFocusAnimated(_animations->widgetStateEngine().isAnimated(widget, AnimationFocus)); 5391 const qreal opacity(_animations->widgetStateEngine().opacity(widget, AnimationFocus)); 5392 // focus color 5393 QColor focusColor; 5394 if (isFocusAnimated) { 5395 focusColor = _helper->alphaColor(_helper->focusColor(palette), opacity); 5396 } else if (hasFocus) { 5397 focusColor = _helper->focusColor(palette); 5398 } 5399 5400 // render focus 5401 _helper->renderFocusLine(painter, focusRect, focusColor); 5402 5403 return true; 5404 } 5405 5406 //___________________________________________________________________________________ 5407 bool Style::drawComboBoxLabelControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5408 { 5409 const auto comboBoxOption(qstyleoption_cast<const QStyleOptionComboBox *>(option)); 5410 if (!comboBoxOption) { 5411 return false; 5412 } 5413 if (comboBoxOption->editable) { 5414 return false; 5415 } 5416 5417 // need to alter palette for focused buttons 5418 const State &state(option->state); 5419 const bool enabled(state & State_Enabled); 5420 const bool sunken(state & (State_On | State_Sunken)); 5421 const bool mouseOver(enabled && (option->state & State_MouseOver)); 5422 const bool hasFocus(enabled && !mouseOver && (option->state & State_HasFocus)); 5423 const bool flat(!comboBoxOption->frame); 5424 5425 QPalette::ColorRole textRole; 5426 if (flat) { 5427 if (hasFocus && sunken) { 5428 textRole = QPalette::WindowText; 5429 } else { 5430 textRole = QPalette::WindowText; 5431 } 5432 5433 } else if (hasFocus) { 5434 textRole = QPalette::ButtonText; 5435 } else { 5436 textRole = QPalette::ButtonText; 5437 } 5438 5439 // change pen color directly 5440 painter->setPen(QPen(option->palette.color(textRole), 1)); 5441 5442 if (const auto cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { 5443 auto editRect = proxy()->subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget); 5444 painter->save(); 5445 painter->setClipRect(editRect); 5446 if (!cb->currentIcon.isNull()) { 5447 QIcon::Mode mode; 5448 5449 if (!enabled) { 5450 mode = QIcon::Disabled; 5451 } else { 5452 mode = QIcon::Normal; 5453 } 5454 5455 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 5456 const QPixmap pixmap = _helper->coloredIcon(cb->currentIcon, cb->palette, cb->iconSize, dpr, mode); 5457 auto iconRect(editRect); 5458 iconRect.setWidth(cb->iconSize.width() + 4); 5459 iconRect = alignedRect(cb->direction, Qt::AlignLeft | Qt::AlignVCenter, iconRect.size(), editRect); 5460 if (cb->editable) { 5461 painter->fillRect(iconRect, option->palette.brush(QPalette::Base)); 5462 } 5463 proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); 5464 5465 if (cb->direction == Qt::RightToLeft) { 5466 editRect.translate(-4 - cb->iconSize.width(), 0); 5467 } else { 5468 editRect.translate(cb->iconSize.width() + 4, 0); 5469 } 5470 } 5471 if (!cb->currentText.isEmpty() && !cb->editable) { 5472 proxy()->drawItemText(painter, 5473 editRect.adjusted(1, 0, -1, 0), 5474 visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter), 5475 cb->palette, 5476 cb->state & State_Enabled, 5477 cb->currentText); 5478 } 5479 painter->restore(); 5480 } 5481 5482 return true; 5483 } 5484 5485 //___________________________________________________________________________________ 5486 bool Style::drawMenuBarItemControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5487 { 5488 // cast option and check 5489 const auto menuItemOption = qstyleoption_cast<const QStyleOptionMenuItem *>(option); 5490 if (!menuItemOption) { 5491 return true; 5492 } 5493 5494 // copy rect and palette 5495 const auto &rect(option->rect); 5496 const auto &palette(option->palette); 5497 5498 // store state 5499 const State &state(option->state); 5500 const bool enabled(state & State_Enabled); 5501 const bool selected(enabled && (state & State_Selected)); 5502 const bool sunken(enabled && (state & State_Sunken)); 5503 const bool useStrongFocus(StyleConfigData::menuItemDrawStrongFocus()); 5504 5505 painter->save(); 5506 painter->setRenderHints(QPainter::Antialiasing); 5507 5508 // render hover and focus 5509 if (useStrongFocus && (selected || sunken)) { 5510 QColor outlineColor; 5511 if (sunken) { 5512 outlineColor = _helper->focusColor(palette); 5513 } else if (selected) { 5514 outlineColor = _helper->hoverColor(palette); 5515 } 5516 _helper->renderFocusRect(painter, rect, outlineColor); 5517 } 5518 5519 /* 5520 check if item as an icon, in which case only the icon should be rendered 5521 consistently with comment in QMenuBarPrivate::calcActionRects 5522 */ 5523 if (!menuItemOption->icon.isNull()) { 5524 // icon size is forced to SmallIconSize 5525 const auto iconSize = pixelMetric(QStyle::PM_SmallIconSize, nullptr, widget); 5526 const auto iconRect = centerRect(rect, iconSize, iconSize); 5527 5528 // decide icon mode and state 5529 QIcon::Mode iconMode; 5530 QIcon::State iconState; 5531 if (!enabled) { 5532 iconMode = QIcon::Disabled; 5533 iconState = QIcon::Off; 5534 5535 } else { 5536 if (useStrongFocus && sunken) { 5537 iconMode = QIcon::Selected; 5538 } else if (useStrongFocus && selected) { 5539 iconMode = QIcon::Active; 5540 } else { 5541 iconMode = QIcon::Normal; 5542 } 5543 5544 iconState = sunken ? QIcon::On : QIcon::Off; 5545 } 5546 5547 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 5548 const auto pixmap = _helper->coloredIcon(menuItemOption->icon, menuItemOption->palette, iconRect.size(), dpr, iconMode, iconState); 5549 drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); 5550 5551 // render outline 5552 if (!useStrongFocus && (selected || sunken)) { 5553 QColor outlineColor; 5554 if (sunken) { 5555 outlineColor = _helper->focusColor(palette); 5556 } else if (selected) { 5557 outlineColor = _helper->hoverColor(palette); 5558 } 5559 5560 _helper->renderFocusLine(painter, iconRect, outlineColor); 5561 } 5562 5563 } else { 5564 // get text rect 5565 const int textFlags(Qt::AlignCenter | _mnemonics->textFlags()); 5566 const auto textRect = option->fontMetrics.boundingRect(rect, textFlags, menuItemOption->text); 5567 5568 // render text 5569 const QPalette::ColorRole role = (useStrongFocus && sunken) ? QPalette::HighlightedText : QPalette::WindowText; 5570 drawItemText(painter, textRect, textFlags, palette, enabled, menuItemOption->text, role); 5571 5572 // render outline 5573 if (!useStrongFocus && (selected || sunken)) { 5574 QColor outlineColor; 5575 if (sunken) { 5576 outlineColor = _helper->focusColor(palette); 5577 } else if (selected) { 5578 outlineColor = _helper->hoverColor(palette); 5579 } 5580 5581 _helper->renderFocusLine(painter, textRect, outlineColor); 5582 } 5583 } 5584 5585 painter->restore(); 5586 5587 return true; 5588 } 5589 5590 //___________________________________________________________________________________ 5591 bool Style::drawMenuItemControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5592 { 5593 // cast option and check 5594 const auto menuItemOption = qstyleoption_cast<const QStyleOptionMenuItem *>(option); 5595 if (!menuItemOption) { 5596 return true; 5597 } 5598 if (menuItemOption->menuItemType == QStyleOptionMenuItem::EmptyArea) { 5599 return true; 5600 } 5601 5602 // Size should be from sizeFromContents(CT_MenuItem, ...), 5603 // but with width set to the widest menu item width. 5604 // see QMenuPrivate::updateActionRects() 5605 const auto &rect = option->rect; 5606 const auto &palette = option->palette; 5607 5608 // store state 5609 const State &state = option->state; 5610 const bool enabled = state & State_Enabled; 5611 const bool selected = enabled && (state & State_Selected); 5612 const bool sunken = enabled && (state & (State_Sunken)); 5613 const bool reverseLayout = option->direction == Qt::RightToLeft; 5614 const bool useStrongFocus = StyleConfigData::menuItemDrawStrongFocus(); 5615 5616 // deal with separators 5617 if (menuItemOption->menuItemType == QStyleOptionMenuItem::Separator) { 5618 auto contentsRect = rect.adjusted(Metrics::MenuItem_MarginWidth, 0, 5619 -Metrics::MenuItem_MarginWidth, 0); 5620 QColor separatorColor; 5621 if (StyleConfigData::menuOpacity() < 100) { 5622 separatorColor = _helper->alphaColor(palette.color(QPalette::WindowText), Metrics::Bias_Default); 5623 } else { 5624 separatorColor = _helper->separatorColor(palette); 5625 } 5626 5627 if (!menuItemOption->text.isEmpty()) { // icon is ignored on purpose 5628 contentsRect.adjust(0, Metrics::MenuItem_MarginHeight, 0, 0); 5629 int flags = visualAlignment(option->direction, Qt::AlignLeft) | Qt::AlignVCenter; 5630 flags |= Qt::TextSingleLine | Qt::TextHideMnemonic | Qt::TextDontClip; 5631 auto font = menuItemOption->font; 5632 font.setBold(true); 5633 QFontMetrics fm(font); 5634 auto textRect = fm.boundingRect(contentsRect, flags, menuItemOption->text); 5635 const auto &labelColor = palette.color(QPalette::Current, QPalette::WindowText); 5636 painter->setFont(font); 5637 painter->setBrush(Qt::NoBrush); 5638 // 0.7 is from Kirigami ListSectionHeader. 5639 // Not using painter->setOpacity() because it disables text antialiasing. 5640 painter->setPen(_helper->alphaColor(labelColor, 0.7)); 5641 painter->drawText(textRect, flags, menuItemOption->text); 5642 // Make spacing between label and line the same as 5643 // the margin from the label to the left menu outline. 5644 // This is intentionally not factored into CT_MenuItem 5645 // size calculations since the line is meant to fill the 5646 // remaining available area after the label has been rendered. 5647 const qreal spacing = pixelMetric(PM_MenuHMargin, option, widget) 5648 + Metrics::MenuItem_MarginWidth - 1; 5649 if (reverseLayout) { 5650 contentsRect.setRight(textRect.left() - spacing); 5651 } else { 5652 contentsRect.setLeft(textRect.right() + spacing); 5653 } 5654 } 5655 5656 _helper->renderSeparator(painter, contentsRect, separatorColor); 5657 return true; 5658 } 5659 5660 // render hover and focus 5661 if (useStrongFocus && (selected || sunken)) { 5662 auto color = _helper->focusColor(palette); 5663 color = _helper->alphaColor(color, 0.3); 5664 const auto outlineColor = _helper->focusOutlineColor(palette); 5665 5666 Sides sides; 5667 if (!menuItemOption->menuRect.isNull()) { 5668 const auto seamlessEdges = _helper->menuSeamlessEdges(widget); 5669 5670 if (rect.top() <= menuItemOption->menuRect.top() && !seamlessEdges.testFlag(Qt::TopEdge)) { 5671 sides |= SideTop; 5672 } 5673 if (rect.bottom() >= menuItemOption->menuRect.bottom() && !seamlessEdges.testFlag(Qt::BottomEdge)) { 5674 sides |= SideBottom; 5675 } 5676 if (rect.left() <= menuItemOption->menuRect.left() && !seamlessEdges.testFlag(Qt::LeftEdge)) { 5677 sides |= SideLeft; 5678 } 5679 if (rect.right() >= menuItemOption->menuRect.right() && !seamlessEdges.testFlag(Qt::RightEdge)) { 5680 sides |= SideRight; 5681 } 5682 } 5683 5684 _helper->renderFocusRect(painter, rect, color, outlineColor, SideTop | SideBottom | SideLeft | SideRight); 5685 } 5686 5687 // get rect available for contents 5688 auto contentsRect(insideMargin(rect, Metrics::MenuItem_MarginWidth, (isTabletMode() ? 2 : 1) * Metrics::MenuItem_MarginHeight)); 5689 5690 // define relevant rectangles 5691 // checkbox 5692 QRect checkBoxRect; 5693 if (menuItemOption->menuHasCheckableItems) { 5694 checkBoxRect = QRect(contentsRect.left(), 5695 contentsRect.top() + (contentsRect.height() - Metrics::CheckBox_Size) / 2, 5696 Metrics::CheckBox_Size, 5697 Metrics::CheckBox_Size); 5698 contentsRect.setLeft(checkBoxRect.right() + Metrics::MenuItem_ItemSpacing + 1); 5699 } 5700 5701 // render checkbox indicator 5702 if (menuItemOption->checkType == QStyleOptionMenuItem::NonExclusive) { 5703 checkBoxRect = visualRect(option, checkBoxRect); 5704 5705 // checkbox state 5706 5707 CheckBoxState state(menuItemOption->checked ? CheckOn : CheckOff); 5708 _helper->renderCheckBoxBackground(painter, checkBoxRect, palette, state, false, sunken); 5709 _helper->renderCheckBox(painter, checkBoxRect, palette, false, state, state, false, sunken); 5710 5711 } else if (menuItemOption->checkType == QStyleOptionMenuItem::Exclusive) { 5712 checkBoxRect = visualRect(option, checkBoxRect); 5713 5714 const bool active(menuItemOption->checked); 5715 _helper->renderRadioButtonBackground(painter, checkBoxRect, palette, active ? RadioOn : RadioOff, false, sunken); 5716 _helper->renderRadioButton(painter, checkBoxRect, palette, false, active ? RadioOn : RadioOff, false, sunken); 5717 } 5718 5719 // icon 5720 int iconMetric = 0; 5721 int iconWidth = 0; 5722 const bool showIcon(showIconsInMenuItems()); 5723 if (showIcon) { 5724 iconMetric = pixelMetric(PM_SmallIconSize, option, widget); 5725 iconWidth = isQtQuickControl(option, widget) ? qMax(iconMetric, menuItemOption->maxIconWidth) : menuItemOption->maxIconWidth; 5726 } 5727 5728 QRect iconRect; 5729 if (showIcon && iconWidth > 0) { 5730 iconRect = QRect(contentsRect.left(), contentsRect.top() + (contentsRect.height() - iconWidth) / 2, iconWidth, iconWidth); 5731 contentsRect.setLeft(iconRect.right() + Metrics::MenuItem_ItemSpacing + 1); 5732 const QSize iconSize(iconMetric, iconMetric); 5733 iconRect = centerRect(iconRect, iconSize); 5734 } else { 5735 contentsRect.setLeft(contentsRect.left() + Metrics::MenuItem_ExtraLeftMargin); 5736 } 5737 5738 if (showIcon && !menuItemOption->icon.isNull()) { 5739 iconRect = visualRect(option, iconRect); 5740 5741 // icon mode 5742 QIcon::Mode mode; 5743 if (enabled) { 5744 mode = QIcon::Normal; 5745 } else { 5746 mode = QIcon::Disabled; 5747 } 5748 5749 // icon state 5750 const QIcon::State iconState(sunken ? QIcon::On : QIcon::Off); 5751 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 5752 const QPixmap pixmap = _helper->coloredIcon(menuItemOption->icon, menuItemOption->palette, iconRect.size(), dpr, mode, iconState); 5753 drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); 5754 } 5755 5756 // arrow 5757 QRect arrowRect(contentsRect.right() - Metrics::MenuButton_IndicatorWidth + 1, 5758 contentsRect.top() + (contentsRect.height() - Metrics::MenuButton_IndicatorWidth) / 2, 5759 Metrics::MenuButton_IndicatorWidth, 5760 Metrics::MenuButton_IndicatorWidth); 5761 contentsRect.setRight(arrowRect.left() - Metrics::MenuItem_ItemSpacing - 1); 5762 5763 if (menuItemOption->menuItemType == QStyleOptionMenuItem::SubMenu) { 5764 // apply right-to-left layout 5765 arrowRect = visualRect(option, arrowRect); 5766 5767 // arrow orientation 5768 const ArrowOrientation orientation(reverseLayout ? ArrowLeft : ArrowRight); 5769 5770 // color 5771 const QColor arrowColor = _helper->arrowColor(palette, QPalette::WindowText); 5772 5773 // render 5774 _helper->renderArrow(painter, arrowRect, arrowColor, orientation); 5775 } 5776 5777 // text 5778 auto textRect = contentsRect; 5779 if (!menuItemOption->text.isEmpty()) { 5780 // adjust textRect 5781 QString text = menuItemOption->text; 5782 textRect = centerRect(textRect, textRect.width(), option->fontMetrics.size(_mnemonics->textFlags(), text).height()); 5783 textRect = visualRect(option, textRect); 5784 5785 // set font 5786 painter->setFont(menuItemOption->font); 5787 5788 // color role 5789 const QPalette::ColorRole role = QPalette::WindowText; 5790 5791 // locate accelerator and render 5792 const int tabPosition(text.indexOf(QLatin1Char('\t'))); 5793 if (tabPosition >= 0) { 5794 const int textFlags(Qt::AlignVCenter | Qt::AlignRight); 5795 QString accelerator(text.mid(tabPosition + 1)); 5796 text = text.left(tabPosition); 5797 painter->save(); 5798 painter->setOpacity(0.7); 5799 drawItemText(painter, textRect, textFlags, palette, enabled, accelerator, role); 5800 painter->restore(); 5801 } 5802 5803 // render text 5804 const int textFlags(Qt::AlignVCenter | (reverseLayout ? Qt::AlignRight : Qt::AlignLeft) | _mnemonics->textFlags()); 5805 textRect = option->fontMetrics.boundingRect(textRect, textFlags, text); 5806 drawItemText(painter, textRect, textFlags, palette, enabled, text, role); 5807 5808 // render hover and focus 5809 if (!useStrongFocus && (selected || sunken)) { 5810 QColor outlineColor; 5811 if (sunken) { 5812 outlineColor = _helper->focusColor(palette); 5813 } else if (selected) { 5814 outlineColor = _helper->hoverColor(palette); 5815 } 5816 5817 _helper->renderFocusLine(painter, textRect, outlineColor); 5818 } 5819 } 5820 5821 return true; 5822 } 5823 5824 //___________________________________________________________________________________ 5825 bool Style::drawProgressBarControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5826 { 5827 const auto progressBarOption(qstyleoption_cast<const QStyleOptionProgressBar *>(option)); 5828 if (!progressBarOption) { 5829 return true; 5830 } 5831 5832 // render groove 5833 QStyleOptionProgressBar progressBarOption2 = *progressBarOption; 5834 progressBarOption2.rect = subElementRect(SE_ProgressBarGroove, progressBarOption, widget); 5835 drawControl(CE_ProgressBarGroove, &progressBarOption2, painter, widget); 5836 5837 const QObject *styleObject(widget ? widget : progressBarOption->styleObject); 5838 5839 const bool busy(progressBarOption->minimum == 0 && progressBarOption->maximum == 0); 5840 5841 // enable busy animations 5842 // need to check both widget and passed styleObject, used for QML 5843 if (styleObject && _animations->busyIndicatorEngine().enabled()) { 5844 // register QML object if defined 5845 if (!widget && progressBarOption->styleObject) { 5846 _animations->busyIndicatorEngine().registerWidget(progressBarOption->styleObject); 5847 } 5848 5849 _animations->busyIndicatorEngine().setAnimated(styleObject, busy); 5850 } 5851 5852 // check if animated and pass to option 5853 if (_animations->busyIndicatorEngine().isAnimated(styleObject)) { 5854 progressBarOption2.progress = _animations->busyIndicatorEngine().value(); 5855 } 5856 5857 // render contents 5858 progressBarOption2.rect = subElementRect(SE_ProgressBarContents, progressBarOption, widget); 5859 drawControl(CE_ProgressBarContents, &progressBarOption2, painter, widget); 5860 5861 // render text 5862 const bool textVisible(progressBarOption->textVisible); 5863 if (textVisible && !busy) { 5864 progressBarOption2.rect = subElementRect(SE_ProgressBarLabel, progressBarOption, widget); 5865 drawControl(CE_ProgressBarLabel, &progressBarOption2, painter, widget); 5866 } 5867 5868 return true; 5869 } 5870 5871 //___________________________________________________________________________________ 5872 bool Style::drawProgressBarContentsControl(const QStyleOption *option, QPainter *painter, const QWidget *) const 5873 { 5874 const auto progressBarOption(qstyleoption_cast<const QStyleOptionProgressBar *>(option)); 5875 if (!progressBarOption) { 5876 return true; 5877 } 5878 5879 // copy rect and palette 5880 auto rect(option->rect); 5881 const auto &palette(option->palette); 5882 5883 // get direction 5884 const bool horizontal(BreezePrivate::isProgressBarHorizontal(progressBarOption)); 5885 const bool inverted(progressBarOption->invertedAppearance); 5886 bool reverse = horizontal && option->direction == Qt::RightToLeft; 5887 if (inverted) { 5888 reverse = !reverse; 5889 } 5890 5891 // check if anything is to be drawn 5892 const bool busy((progressBarOption->minimum == 0 && progressBarOption->maximum == 0)); 5893 if (busy) { 5894 const int progress(_animations->busyIndicatorEngine().value()); 5895 5896 const auto &first = palette.color(QPalette::Highlight); 5897 const auto second(KColorUtils::mix(palette.color(QPalette::Highlight), palette.color(QPalette::Window), 0.7)); 5898 _helper->renderProgressBarBusyContents(painter, rect, first, second, horizontal, reverse, progress); 5899 5900 } else { 5901 const QRegion oldClipRegion(painter->clipRegion()); 5902 if (horizontal) { 5903 if (rect.width() < Metrics::ProgressBar_Thickness) { 5904 painter->setClipRect(rect, Qt::IntersectClip); 5905 if (reverse) { 5906 rect.setLeft(rect.left() - Metrics::ProgressBar_Thickness + rect.width()); 5907 } else { 5908 rect.setWidth(Metrics::ProgressBar_Thickness); 5909 } 5910 } 5911 5912 } else { 5913 if (rect.height() < Metrics::ProgressBar_Thickness) { 5914 painter->setClipRect(rect, Qt::IntersectClip); 5915 if (reverse) { 5916 rect.setHeight(Metrics::ProgressBar_Thickness); 5917 } else { 5918 rect.setTop(rect.top() - Metrics::ProgressBar_Thickness + rect.height()); 5919 } 5920 } 5921 } 5922 5923 auto contentsColor(option->state.testFlag(QStyle::State_Selected) ? palette.color(QPalette::HighlightedText) : palette.color(QPalette::Highlight)); 5924 5925 _helper->renderProgressBarGroove(painter, rect, contentsColor, palette.color(QPalette::Window)); 5926 painter->setClipRegion(oldClipRegion); 5927 } 5928 5929 return true; 5930 } 5931 5932 //___________________________________________________________________________________ 5933 bool Style::drawProgressBarGrooveControl(const QStyleOption *option, QPainter *painter, const QWidget *) const 5934 { 5935 const auto &palette(option->palette); 5936 const auto color(_helper->alphaColor(palette.color(QPalette::WindowText), 0.2)); 5937 _helper->renderProgressBarGroove(painter, option->rect, color, palette.color(QPalette::Window)); 5938 return true; 5939 } 5940 5941 //___________________________________________________________________________________ 5942 bool Style::drawProgressBarLabelControl(const QStyleOption *option, QPainter *painter, const QWidget *) const 5943 { 5944 // cast option and check 5945 const auto progressBarOption(qstyleoption_cast<const QStyleOptionProgressBar *>(option)); 5946 if (!progressBarOption) { 5947 return true; 5948 } 5949 5950 // get direction and check 5951 const bool horizontal(BreezePrivate::isProgressBarHorizontal(progressBarOption)); 5952 if (!horizontal) { 5953 return true; 5954 } 5955 5956 // store rect and palette 5957 const auto &rect(option->rect); 5958 const auto &palette(option->palette); 5959 5960 // store state and direction 5961 const State &state(option->state); 5962 const bool enabled(state & State_Enabled); 5963 5964 // define text rect 5965 const Qt::Alignment hAlign((progressBarOption->textAlignment == Qt::AlignLeft) ? Qt::AlignHCenter : progressBarOption->textAlignment); 5966 const QPalette::ColorRole role(progressBarOption->state.testFlag(QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text); 5967 drawItemText(painter, rect, Qt::AlignVCenter | hAlign, palette, enabled, progressBarOption->text, role); 5968 5969 return true; 5970 } 5971 5972 //___________________________________________________________________________________ 5973 bool Style::drawScrollBarSliderControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 5974 { 5975 // cast option and check 5976 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 5977 if (!sliderOption) { 5978 return true; 5979 } 5980 5981 // copy rect and palette 5982 // const auto& rect( option->rect ); 5983 const auto &palette(option->palette); 5984 5985 // need to make it center due to the thin line separator 5986 QRect rect = option->rect; 5987 5988 if (option->state & State_Horizontal) { 5989 rect.setTop(PenWidth::Frame); 5990 } else if (option->direction == Qt::RightToLeft) { 5991 rect.setRight(rect.right() - qRound(PenWidth::Frame)); 5992 } else { 5993 rect.setLeft(PenWidth::Frame); 5994 } 5995 5996 // try to understand if anywhere the widget is under mouse, not just the handle, use _animations in case of QWidget, option->styleObject in case of QML 5997 bool widgetMouseOver((option->state & State_MouseOver)); 5998 if (widget) { 5999 widgetMouseOver = _animations->scrollBarEngine().isHovered(widget, QStyle::SC_ScrollBarGroove); 6000 } else if (option->styleObject) { 6001 widgetMouseOver = option->styleObject->property("hover").toBool(); 6002 } 6003 6004 qreal grooveAnimationOpacity(_animations->scrollBarEngine().opacity(widget, QStyle::SC_ScrollBarGroove)); 6005 if (grooveAnimationOpacity == AnimationData::OpacityInvalid) { 6006 grooveAnimationOpacity = (widgetMouseOver ? 1 : 0); 6007 } 6008 6009 // define handle rect 6010 QRect handleRect; 6011 const State &state(option->state); 6012 const bool horizontal(state & State_Horizontal); 6013 if (horizontal) { 6014 handleRect = centerRect(rect, rect.width(), Metrics::ScrollBar_SliderWidth); 6015 } else { 6016 handleRect = centerRect(rect, Metrics::ScrollBar_SliderWidth, rect.height()); 6017 } 6018 6019 const bool enabled(state & State_Enabled); 6020 const bool mouseOver(enabled && (state & State_MouseOver)); 6021 6022 // check focus from relevant parent 6023 const QWidget *parent(scrollBarParent(widget)); 6024 const bool hasFocus(enabled && ((widget && widget->hasFocus()) || (parent && parent->hasFocus()))); 6025 6026 // enable animation state 6027 const bool handleActive(sliderOption->activeSubControls & SC_ScrollBarSlider); 6028 _animations->scrollBarEngine().updateState(widget, AnimationFocus, hasFocus); 6029 6030 _animations->scrollBarEngine().updateState(widget, AnimationHover, mouseOver && handleActive); 6031 6032 const auto mode(_animations->scrollBarEngine().animationMode(widget, SC_ScrollBarSlider)); 6033 const qreal opacity(_animations->scrollBarEngine().opacity(widget, SC_ScrollBarSlider)); 6034 auto color = _helper->scrollBarHandleColor(palette, mouseOver, hasFocus, opacity, mode); 6035 if (StyleConfigData::animationsEnabled()) { 6036 color.setAlphaF(color.alphaF() * (0.7 + 0.3 * grooveAnimationOpacity)); 6037 } 6038 6039 _helper->renderScrollBarHandle(painter, handleRect, color, palette.color(QPalette::Window)); 6040 return true; 6041 } 6042 6043 //___________________________________________________________________________________ 6044 bool Style::drawScrollBarAddLineControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6045 { 6046 // do nothing if no buttons are defined 6047 if (_addLineButtons == NoButton) { 6048 return true; 6049 } 6050 6051 // cast option and check 6052 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 6053 if (!sliderOption) { 6054 return true; 6055 } 6056 6057 const State &state(option->state); 6058 const bool horizontal(state & State_Horizontal); 6059 const bool reverseLayout(option->direction == Qt::RightToLeft); 6060 6061 // adjust rect, based on number of buttons to be drawn 6062 auto rect(scrollBarInternalSubControlRect(sliderOption, SC_ScrollBarAddLine)); 6063 6064 // need to make it center due to the thin line separator 6065 if (option->state & State_Horizontal) { 6066 rect.setTop(PenWidth::Frame); 6067 } else if (option->direction == Qt::RightToLeft) { 6068 rect.setRight(rect.right() - qRound(PenWidth::Frame)); 6069 } else { 6070 rect.setLeft(PenWidth::Frame); 6071 } 6072 6073 QColor color; 6074 QStyleOptionSlider copy(*sliderOption); 6075 if (_addLineButtons == DoubleButton) { 6076 if (horizontal) { 6077 // Draw the arrows 6078 const QSize halfSize(rect.width() / 2, rect.height()); 6079 const QRect leftSubButton(rect.topLeft(), halfSize); 6080 const QRect rightSubButton(leftSubButton.topRight() + QPoint(1, 0), halfSize); 6081 6082 copy.rect = leftSubButton; 6083 color = scrollBarArrowColor(©, reverseLayout ? SC_ScrollBarAddLine : SC_ScrollBarSubLine, widget); 6084 _helper->renderArrow(painter, leftSubButton, color, ArrowLeft); 6085 6086 copy.rect = rightSubButton; 6087 color = scrollBarArrowColor(©, reverseLayout ? SC_ScrollBarSubLine : SC_ScrollBarAddLine, widget); 6088 _helper->renderArrow(painter, rightSubButton, color, ArrowRight); 6089 6090 } else { 6091 const QSize halfSize(rect.width(), rect.height() / 2); 6092 const QRect topSubButton(rect.topLeft(), halfSize); 6093 const QRect botSubButton(topSubButton.bottomLeft() + QPoint(0, 1), halfSize); 6094 6095 copy.rect = topSubButton; 6096 color = scrollBarArrowColor(©, SC_ScrollBarSubLine, widget); 6097 _helper->renderArrow(painter, topSubButton, color, ArrowUp); 6098 6099 copy.rect = botSubButton; 6100 color = scrollBarArrowColor(©, SC_ScrollBarAddLine, widget); 6101 _helper->renderArrow(painter, botSubButton, color, ArrowDown); 6102 } 6103 6104 } else if (_addLineButtons == SingleButton) { 6105 copy.rect = rect; 6106 color = scrollBarArrowColor(©, SC_ScrollBarAddLine, widget); 6107 if (horizontal) { 6108 if (reverseLayout) { 6109 _helper->renderArrow(painter, rect, color, ArrowLeft); 6110 } else { 6111 _helper->renderArrow(painter, rect.translated(1, 0), color, ArrowRight); 6112 } 6113 6114 } else { 6115 _helper->renderArrow(painter, rect.translated(0, 1), color, ArrowDown); 6116 } 6117 } 6118 6119 return true; 6120 } 6121 6122 //___________________________________________________________________________________ 6123 bool Style::drawScrollBarSubLineControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6124 { 6125 // do nothing if no buttons are set 6126 if (_subLineButtons == NoButton) { 6127 return true; 6128 } 6129 6130 // cast option and check 6131 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 6132 if (!sliderOption) { 6133 return true; 6134 } 6135 6136 const State &state(option->state); 6137 const bool horizontal(state & State_Horizontal); 6138 const bool reverseLayout(option->direction == Qt::RightToLeft); 6139 6140 // adjust rect, based on number of buttons to be drawn 6141 auto rect(scrollBarInternalSubControlRect(sliderOption, SC_ScrollBarSubLine)); 6142 6143 // need to make it center due to the thin line separator 6144 if (option->state & State_Horizontal) { 6145 rect.setTop(PenWidth::Frame); 6146 } else if (option->direction == Qt::RightToLeft) { 6147 rect.setRight(rect.right() - qRound(PenWidth::Frame)); 6148 } else { 6149 rect.setLeft(PenWidth::Frame); 6150 } 6151 6152 QColor color; 6153 QStyleOptionSlider copy(*sliderOption); 6154 if (_subLineButtons == DoubleButton) { 6155 if (horizontal) { 6156 // Draw the arrows 6157 const QSize halfSize(rect.width() / 2, rect.height()); 6158 const QRect leftSubButton(rect.topLeft(), halfSize); 6159 const QRect rightSubButton(leftSubButton.topRight() + QPoint(1, 0), halfSize); 6160 6161 copy.rect = leftSubButton; 6162 color = scrollBarArrowColor(©, reverseLayout ? SC_ScrollBarAddLine : SC_ScrollBarSubLine, widget); 6163 _helper->renderArrow(painter, leftSubButton, color, ArrowLeft); 6164 6165 copy.rect = rightSubButton; 6166 color = scrollBarArrowColor(©, reverseLayout ? SC_ScrollBarSubLine : SC_ScrollBarAddLine, widget); 6167 _helper->renderArrow(painter, rightSubButton, color, ArrowRight); 6168 6169 } else { 6170 const QSize halfSize(rect.width(), rect.height() / 2); 6171 const QRect topSubButton(rect.topLeft(), halfSize); 6172 const QRect botSubButton(topSubButton.bottomLeft() + QPoint(0, 1), halfSize); 6173 6174 copy.rect = topSubButton; 6175 color = scrollBarArrowColor(©, SC_ScrollBarSubLine, widget); 6176 _helper->renderArrow(painter, topSubButton, color, ArrowUp); 6177 6178 copy.rect = botSubButton; 6179 color = scrollBarArrowColor(©, SC_ScrollBarAddLine, widget); 6180 _helper->renderArrow(painter, botSubButton, color, ArrowDown); 6181 } 6182 6183 } else if (_subLineButtons == SingleButton) { 6184 copy.rect = rect; 6185 color = scrollBarArrowColor(©, SC_ScrollBarSubLine, widget); 6186 if (horizontal) { 6187 if (reverseLayout) { 6188 _helper->renderArrow(painter, rect.translated(1, 0), color, ArrowRight); 6189 } else { 6190 _helper->renderArrow(painter, rect, color, ArrowLeft); 6191 } 6192 6193 } else { 6194 _helper->renderArrow(painter, rect, color, ArrowUp); 6195 } 6196 } 6197 6198 return true; 6199 } 6200 6201 //___________________________________________________________________________________ 6202 bool Style::drawShapedFrameControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6203 { 6204 // cast option and check 6205 const auto frameOpt = qstyleoption_cast<const QStyleOptionFrame *>(option); 6206 if (!frameOpt) { 6207 return false; 6208 } 6209 6210 switch (frameOpt->frameShape) { 6211 case QFrame::Box: { 6212 if (option->state & State_Sunken) { 6213 return true; 6214 } else { 6215 break; 6216 } 6217 } 6218 6219 case QFrame::HLine: 6220 case QFrame::VLine: { 6221 const auto &rect(option->rect); 6222 const auto color(_helper->separatorColor(option->palette)); 6223 const bool isVertical(frameOpt->frameShape == QFrame::VLine); 6224 _helper->renderSeparator(painter, rect, color, isVertical); 6225 return true; 6226 } 6227 6228 case QFrame::StyledPanel: { 6229 if (isQtQuickControl(option, widget) && option->styleObject->property("elementType").toString() == QLatin1String("combobox")) { 6230 // ComboBox popup frame 6231 drawFrameMenuPrimitive(option, painter, widget); 6232 return true; 6233 } 6234 6235 // pixelMetric(PM_DefaultFrameWidth) contains heuristics to decide 6236 // when to draw a frame and return 0 when we shouldn't draw a frame. 6237 return pixelMetric(PM_DefaultFrameWidth, option, widget) == 0; 6238 } 6239 6240 default: 6241 break; 6242 } 6243 6244 return false; 6245 } 6246 6247 // Adapted from QMacStylePrivate::drawFocusRing() 6248 bool Style::drawFocusFrame(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6249 { 6250 const QFocusFrame *focusFrame = qobject_cast<const QFocusFrame *>(widget); 6251 const QWidget *targetWidget = focusFrame ? focusFrame->widget() : nullptr; 6252 // If we have a QFocusFrame and no target widget, don't draw anything. 6253 if (focusFrame && !targetWidget) { 6254 return true; 6255 } 6256 6257 const int hmargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, option, widget); 6258 const int vmargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, option, widget); 6259 // Not using targetWidget->rect() to get this because this should still work when widget is null. 6260 const QRect targetWidgetRect = option->rect.adjusted(hmargin, vmargin, -hmargin, -vmargin); 6261 6262 QRect innerRect = targetWidgetRect; 6263 QRect outerRect = option->rect; 6264 6265 qreal innerRadius = Metrics::Frame_FrameRadius; 6266 qreal outerRadius = innerRadius + vmargin; 6267 6268 QPainterPath focusFramePath; 6269 focusFramePath.setFillRule(Qt::OddEvenFill); 6270 6271 // If focusFrame is null, make it still possible to draw something since 6272 // most draw functions allow drawing something when the widget is null 6273 if (!focusFrame) { 6274 focusFramePath.addRoundedRect(innerRect, innerRadius, innerRadius); 6275 focusFramePath.addRoundedRect(outerRect, outerRadius, outerRadius); 6276 } else if (targetWidget->inherits("QLineEdit") || targetWidget->inherits("QTextEdit") || targetWidget->inherits("QAbstractSpinBox") 6277 || targetWidget->inherits("QComboBox") || targetWidget->inherits("QPushButton") || targetWidget->inherits("QToolButton")) { 6278 /* It's OK to check for QAbstractSpinBox instead of more spacific classes 6279 * because QAbstractSpinBox handles the painting for spinboxes, 6280 * unlike most abstract widget classes. 6281 */ 6282 innerRect.adjust(1, 1, -1, -1); 6283 focusFramePath.addRoundedRect(innerRect, innerRadius, innerRadius); 6284 outerRect = innerRect.adjusted(-hmargin, -vmargin, hmargin, vmargin); 6285 focusFramePath.addRoundedRect(outerRect, outerRadius, outerRadius); 6286 } else if (auto checkBox = qobject_cast<const QCheckBox *>(targetWidget)) { 6287 QStyleOptionButton opt; 6288 opt.initFrom(checkBox); 6289 if (checkBox->isDown()) { 6290 opt.state |= QStyle::State_Sunken; 6291 } 6292 if (checkBox->isTristate()) { 6293 opt.state |= QStyle::State_NoChange; 6294 } else if (checkBox->isChecked()) { 6295 opt.state |= QStyle::State_On; 6296 } else { 6297 opt.state |= QStyle::State_Off; 6298 } 6299 opt.text = checkBox->text(); 6300 opt.icon = checkBox->icon(); 6301 opt.iconSize = checkBox->iconSize(); 6302 innerRect = subElementRect(SE_CheckBoxIndicator, &opt, checkBox); 6303 innerRect.adjust(2, 2, -2, -2); 6304 innerRect.translate(hmargin, vmargin); 6305 focusFramePath.addRoundedRect(innerRect, innerRadius, innerRadius); 6306 outerRect = innerRect.adjusted(-hmargin, -vmargin, hmargin, vmargin); 6307 focusFramePath.addRoundedRect(outerRect, outerRadius, outerRadius); 6308 } else if (auto radioButton = qobject_cast<const QRadioButton *>(targetWidget)) { 6309 QStyleOptionButton opt; 6310 opt.initFrom(radioButton); 6311 if (radioButton->isDown()) { 6312 opt.state |= QStyle::State_Sunken; 6313 } 6314 if (radioButton->isChecked()) { 6315 opt.state |= QStyle::State_On; 6316 } else { 6317 opt.state |= QStyle::State_Off; 6318 } 6319 opt.text = radioButton->text(); 6320 opt.icon = radioButton->icon(); 6321 opt.iconSize = radioButton->iconSize(); 6322 innerRect = subElementRect(SE_RadioButtonIndicator, &opt, radioButton); 6323 innerRect.adjust(2, 2, -2, -2); 6324 innerRect.translate(hmargin, vmargin); 6325 innerRadius = innerRect.height() / 2.0; 6326 focusFramePath.addRoundedRect(innerRect, innerRadius, innerRadius); 6327 outerRect = innerRect.adjusted(-hmargin, -vmargin, hmargin, vmargin); 6328 outerRadius = outerRect.height() / 2.0; 6329 focusFramePath.addRoundedRect(outerRect, outerRadius, outerRadius); 6330 } else if (auto slider = qobject_cast<const QSlider *>(targetWidget)) { 6331 QStyleOptionSlider opt; 6332 _helper->initSliderStyleOption(slider, &opt); 6333 innerRect = subControlRect(CC_Slider, &opt, SC_SliderHandle, slider); 6334 auto outerRect = _helper->pathForSliderHandleFocusFrame(focusFramePath, innerRect, hmargin, vmargin); 6335 if (_focusFrame) { 6336 // Workaround QFocusFrame not fully repainting outside the bounds of the targetWidget 6337 auto previousRect = _focusFrame->property("_lastOuterRect").value<QRectF>(); 6338 if (previousRect != outerRect) { 6339 _focusFrame->update(); 6340 _focusFrame->setProperty("_lastOuterRect", outerRect); 6341 } 6342 } 6343 } else if (auto dial = qobject_cast<const QDial *>(targetWidget)) { 6344 QStyleOptionSlider opt; 6345 opt.initFrom(dial); 6346 opt.maximum = dial->maximum(); 6347 opt.minimum = dial->minimum(); 6348 opt.sliderPosition = dial->sliderPosition(); 6349 opt.sliderValue = dial->value(); 6350 opt.singleStep = dial->singleStep(); 6351 opt.pageStep = dial->pageStep(); 6352 opt.upsideDown = !dial->invertedAppearance(); 6353 opt.notchTarget = dial->notchTarget(); 6354 opt.dialWrapping = dial->wrapping(); 6355 if (!dial->notchesVisible()) { 6356 opt.subControls &= ~QStyle::SC_DialTickmarks; 6357 opt.tickPosition = QSlider::TicksAbove; 6358 } else { 6359 opt.tickPosition = QSlider::NoTicks; 6360 } 6361 opt.tickInterval = dial->notchSize(); 6362 innerRect = subControlRect(CC_Dial, &opt, SC_DialHandle, dial); 6363 innerRect.adjust(1, 1, -1, -1); 6364 innerRect.translate(hmargin, vmargin); 6365 innerRadius = innerRect.height() / 2.0; 6366 focusFramePath.addRoundedRect(innerRect, innerRadius, innerRadius); 6367 outerRect = innerRect.adjusted(-hmargin, -vmargin, hmargin, vmargin); 6368 outerRadius = outerRect.height() / 2.0; 6369 focusFramePath.addRoundedRect(outerRect, outerRadius, outerRadius); 6370 if (_focusFrame) { 6371 // Workaround QFocusFrame not fully repainting outside the bounds of the targetWidget 6372 auto previousRect = _focusFrame->property("_lastOuterRect").value<QRect>(); 6373 if (previousRect != outerRect) { 6374 _focusFrame->update(); 6375 _focusFrame->setProperty("_lastOuterRect", outerRect); 6376 } 6377 } 6378 } else if (auto groupBox = qobject_cast<const QGroupBox *>(targetWidget)) { 6379 QStyleOptionGroupBox opt; 6380 opt.initFrom(groupBox); 6381 opt.lineWidth = 1; 6382 opt.midLineWidth = 0; 6383 opt.textAlignment = groupBox->alignment(); 6384 opt.subControls = QStyle::SC_GroupBoxFrame; 6385 if (groupBox->isCheckable()) { 6386 opt.subControls |= QStyle::SC_GroupBoxCheckBox; 6387 if (groupBox->isChecked()) { 6388 opt.state |= QStyle::State_On; 6389 } else { 6390 opt.state |= QStyle::State_Off; 6391 } 6392 // NOTE: we can't get the sunken state of the checkbox 6393 } 6394 opt.text = groupBox->title(); 6395 if (!opt.text.isEmpty()) { 6396 opt.subControls |= QStyle::SC_GroupBoxLabel; 6397 } 6398 innerRect = subControlRect(CC_GroupBox, &opt, SC_GroupBoxCheckBox, groupBox); 6399 innerRect.adjust(2, 2, -2, -2); 6400 innerRect.translate(hmargin, vmargin); 6401 innerRect = visualRect(option, innerRect); 6402 focusFramePath.addRoundedRect(innerRect, innerRadius, innerRadius); 6403 outerRect = innerRect.adjusted(-hmargin, -vmargin, hmargin, vmargin); 6404 focusFramePath.addRoundedRect(outerRect, outerRadius, outerRadius); 6405 } else { 6406 focusFramePath.addRoundedRect(innerRect, innerRadius, innerRadius); 6407 focusFramePath.addRoundedRect(outerRect, outerRadius, outerRadius); 6408 } 6409 6410 auto outerColor = _helper->alphaColor(option->palette.highlight().color(), 0.33); 6411 6412 painter->setRenderHint(QPainter::Antialiasing); 6413 painter->fillPath(focusFramePath, outerColor); 6414 return true; 6415 } 6416 6417 //___________________________________________________________________________________ 6418 bool Style::drawRubberBandControl(const QStyleOption *option, QPainter *painter, const QWidget *) const 6419 { 6420 painter->save(); 6421 painter->setRenderHints(QPainter::Antialiasing); 6422 6423 const auto &palette(option->palette); 6424 const auto outline = KColorUtils::lighten(palette.color(QPalette::Highlight)); 6425 auto background = palette.color(QPalette::Highlight); 6426 background.setAlphaF(0.20); 6427 6428 painter->setPen(outline); 6429 painter->setBrush(background); 6430 painter->drawRoundedRect(_helper->strokedRect(option->rect), Metrics::Frame_FrameRadius, Metrics::Frame_FrameRadius); 6431 6432 painter->restore(); 6433 return true; 6434 } 6435 6436 //___________________________________________________________________________________ 6437 bool Style::drawHeaderSectionControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6438 { 6439 const auto &rect(option->rect); 6440 const auto &palette(option->palette); 6441 const auto &state(option->state); 6442 const bool enabled(state & State_Enabled); 6443 const bool mouseOver(enabled && (state & State_MouseOver)); 6444 const bool sunken(enabled && (state & (State_On | State_Sunken))); 6445 6446 const auto headerOption(qstyleoption_cast<const QStyleOptionHeader *>(option)); 6447 if (!headerOption) { 6448 return true; 6449 } 6450 6451 const bool horizontal(headerOption->orientation == Qt::Horizontal); 6452 const bool isCorner(widget && widget->inherits("QTableCornerButton")); 6453 const bool reverseLayout(option->direction == Qt::RightToLeft); 6454 6455 // update animation state 6456 _animations->headerViewEngine().updateState(widget, rect.topLeft(), mouseOver); 6457 const bool animated(enabled && _animations->headerViewEngine().isAnimated(widget, rect.topLeft())); 6458 const qreal opacity(_animations->headerViewEngine().opacity(widget, rect.topLeft())); 6459 6460 // fill 6461 const auto &normal = palette.color(QPalette::Button); 6462 const auto focus(KColorUtils::mix(normal, _helper->focusColor(palette), 0.2)); 6463 const auto hover(KColorUtils::mix(normal, _helper->hoverColor(palette), 0.2)); 6464 6465 QColor color; 6466 if (sunken) { 6467 color = focus; 6468 } else if (animated) { 6469 color = KColorUtils::mix(normal, hover, opacity); 6470 } else if (mouseOver) { 6471 color = hover; 6472 } else { 6473 color = normal; 6474 } 6475 6476 painter->setRenderHint(QPainter::Antialiasing, false); 6477 painter->setBrush(color); 6478 painter->setPen(Qt::NoPen); 6479 painter->drawRect(rect); 6480 6481 // outline 6482 painter->setBrush(Qt::NoBrush); 6483 painter->setPen(_helper->alphaColor(palette.color(QPalette::WindowText), Metrics::Bias_Default)); 6484 6485 if (isCorner) { 6486 if (reverseLayout) { 6487 painter->drawPoint(rect.bottomLeft()); 6488 } else { 6489 painter->drawPoint(rect.bottomRight()); 6490 } 6491 6492 } else if (horizontal) { 6493 painter->drawLine(rect.bottomLeft(), rect.bottomRight()); 6494 6495 } else { 6496 if (reverseLayout) { 6497 painter->drawLine(rect.topLeft(), rect.bottomLeft()); 6498 } else { 6499 painter->drawLine(rect.topRight(), rect.bottomRight()); 6500 } 6501 } 6502 6503 // separators 6504 painter->setPen(_helper->alphaColor(palette.color(QPalette::WindowText), Metrics::Bias_Default)); 6505 6506 // If the separator would be next to a "HeaderEmptyArea", skip it and let that draw 6507 // the separator instead. This means that those separators are only visible when necessary. 6508 6509 if (horizontal) { 6510 if (headerOption->position != QStyleOptionHeader::OnlyOneSection) { 6511 if (reverseLayout && headerOption->position != QStyleOptionHeader::Beginning) { 6512 painter->drawLine(rect.topLeft(), rect.bottomLeft() - QPoint(0, 1)); 6513 } else if (!reverseLayout && headerOption->position != QStyleOptionHeader::End) { 6514 painter->drawLine(rect.topRight(), rect.bottomRight() - QPoint(0, 1)); 6515 } 6516 } 6517 6518 } else if (headerOption->position != QStyleOptionHeader::End) { 6519 if (reverseLayout) { 6520 painter->drawLine(rect.bottomLeft() + QPoint(1, 0), rect.bottomRight()); 6521 } else { 6522 painter->drawLine(rect.bottomLeft(), rect.bottomRight() - QPoint(1, 0)); 6523 } 6524 } 6525 6526 return true; 6527 } 6528 6529 //___________________________________________________________________________________ 6530 bool Style::drawHeaderEmptyAreaControl(const QStyleOption *option, QPainter *painter, const QWidget *) const 6531 { 6532 // use the same background as in drawHeaderPrimitive 6533 const auto &rect(option->rect); 6534 auto palette(option->palette); 6535 6536 const bool horizontal(option->state & QStyle::State_Horizontal); 6537 const bool reverseLayout(option->direction == Qt::RightToLeft); 6538 6539 // fill 6540 painter->setRenderHint(QPainter::Antialiasing, false); 6541 painter->setBrush(palette.color(QPalette::Button)); 6542 painter->setPen(Qt::NoPen); 6543 painter->drawRect(rect); 6544 6545 // outline 6546 painter->setBrush(Qt::NoBrush); 6547 painter->setPen(_helper->alphaColor(palette.color(QPalette::ButtonText), 0.1)); 6548 6549 if (horizontal) { 6550 painter->drawLine(rect.bottomLeft(), rect.bottomRight()); 6551 6552 } else { 6553 if (reverseLayout) { 6554 painter->drawLine(rect.topLeft(), rect.bottomLeft()); 6555 } else { 6556 painter->drawLine(rect.topRight(), rect.bottomRight()); 6557 } 6558 } 6559 6560 // separators 6561 painter->setPen(_helper->alphaColor(palette.color(QPalette::WindowText), 0.2)); 6562 6563 if (horizontal) { 6564 // 26aa20407d in qtbase introduced a 1px empty area in reversed horizontal headers. Ignore it. 6565 if (reverseLayout && rect.width() != 1) { 6566 painter->drawLine(rect.topRight(), rect.bottomRight() - QPoint(0, 1)); 6567 } else if (!reverseLayout) { 6568 painter->drawLine(rect.topLeft(), rect.bottomLeft() - QPoint(0, 1)); 6569 } 6570 6571 } else { 6572 if (reverseLayout) { 6573 painter->drawLine(rect.topLeft() + QPoint(1, 0), rect.topRight()); 6574 } else { 6575 painter->drawLine(rect.topLeft(), rect.topRight() - QPoint(1, 0)); 6576 } 6577 } 6578 6579 return true; 6580 } 6581 6582 //___________________________________________________________________________________ 6583 bool Style::drawTabBarTabLabelControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6584 { 6585 // call parent style method 6586 6587 // we draw the focus ourselves so we don't need the parent to do it for us. 6588 auto old = option->state; 6589 // clear the State_HasFocus bit 6590 const_cast<QStyleOption *>(option)->state &= ~State_HasFocus; 6591 ParentStyleClass::drawControl(CE_TabBarTabLabel, option, painter, widget); 6592 const_cast<QStyleOption *>(option)->state = old; 6593 6594 // store rect and palette 6595 const auto &rect(option->rect); 6596 const auto &palette(option->palette); 6597 6598 // check focus 6599 const State &state(option->state); 6600 const bool enabled(state & State_Enabled); 6601 const bool selected(state & State_Selected); 6602 const bool hasFocus(enabled && selected && (state & State_HasFocus)); 6603 6604 // update mouse over animation state 6605 _animations->tabBarEngine().updateState(widget, rect.topLeft(), AnimationFocus, hasFocus); 6606 const bool animated(enabled && selected && _animations->tabBarEngine().isAnimated(widget, rect.topLeft(), AnimationFocus)); 6607 const qreal opacity(_animations->tabBarEngine().opacity(widget, rect.topLeft(), AnimationFocus)); 6608 6609 if (!(hasFocus || animated)) { 6610 return true; 6611 } 6612 6613 // code is copied from QCommonStyle, but adds focus 6614 // cast option and check 6615 const auto tabOption(qstyleoption_cast<const QStyleOptionTab *>(option)); 6616 if (!tabOption || tabOption->text.isEmpty()) { 6617 return true; 6618 } 6619 6620 // tab option rect 6621 const bool verticalTabs(isVerticalTab(tabOption)); 6622 const int textFlags(Qt::AlignCenter | _mnemonics->textFlags()); 6623 6624 // text rect 6625 auto textRect(subElementRect(SE_TabBarTabText, option, widget)); 6626 6627 if (verticalTabs) { 6628 // properly rotate painter 6629 painter->save(); 6630 int newX, newY, newRot; 6631 if (tabOption->shape == QTabBar::RoundedEast || tabOption->shape == QTabBar::TriangularEast) { 6632 newX = rect.width() + rect.x(); 6633 newY = rect.y(); 6634 newRot = 90; 6635 6636 } else { 6637 newX = rect.x(); 6638 newY = rect.y() + rect.height(); 6639 newRot = -90; 6640 } 6641 6642 QTransform transform; 6643 transform.translate(newX, newY); 6644 transform.rotate(newRot); 6645 painter->setTransform(transform, true); 6646 } 6647 6648 // adjust text rect based on font metrics 6649 textRect = option->fontMetrics.boundingRect(textRect, textFlags, tabOption->text); 6650 6651 // focus color 6652 QColor focusColor; 6653 if (animated) { 6654 focusColor = _helper->alphaColor(_helper->focusColor(palette), opacity); 6655 } else if (hasFocus) { 6656 focusColor = _helper->focusColor(palette); 6657 } 6658 6659 // render focus line 6660 _helper->renderFocusLine(painter, textRect, focusColor); 6661 6662 if (verticalTabs) { 6663 painter->restore(); 6664 } 6665 6666 return true; 6667 } 6668 6669 //___________________________________________________________________________________ 6670 bool Style::drawTabBarTabShapeControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6671 { 6672 const auto tabOption(qstyleoption_cast<const QStyleOptionTab *>(option)); 6673 if (!tabOption) { 6674 return true; 6675 } 6676 6677 // palette and state 6678 const bool enabled = option->state & State_Enabled; 6679 const bool activeFocus = option->state & State_HasFocus; 6680 const bool visualFocus = activeFocus && option->state & QStyle::State_KeyboardFocusChange; 6681 const bool hovered = option->state & State_MouseOver; 6682 const bool down = option->state & State_Sunken; 6683 const bool selected = option->state & State_Selected; 6684 const bool north = tabOption->shape == QTabBar::RoundedNorth || tabOption->shape == QTabBar::TriangularNorth; 6685 const bool south = tabOption->shape == QTabBar::RoundedSouth || tabOption->shape == QTabBar::TriangularSouth; 6686 const bool west = tabOption->shape == QTabBar::RoundedWest || tabOption->shape == QTabBar::TriangularWest; 6687 const bool east = tabOption->shape == QTabBar::RoundedEast || tabOption->shape == QTabBar::TriangularEast; 6688 6689 // check if tab is being dragged 6690 const bool isDragged(widget && selected && painter->device() != widget); 6691 const bool isLocked(widget && _tabBarData->isLocked(widget)); 6692 6693 // store rect 6694 auto rect(option->rect); 6695 6696 // update mouse over animation state 6697 _animations->tabBarEngine().updateState(widget, rect.topLeft(), AnimationHover, hovered && !selected && enabled); 6698 const qreal animation = _animations->tabBarEngine().opacity(widget, rect.topLeft(), AnimationHover); 6699 6700 // lock state 6701 if (selected && widget && isDragged) { 6702 _tabBarData->lock(widget); 6703 } else if (widget && selected && _tabBarData->isLocked(widget)) { 6704 _tabBarData->release(); 6705 } 6706 6707 // tab position 6708 const QStyleOptionTab::TabPosition &position = tabOption->position; 6709 const bool isSingle(position == QStyleOptionTab::OnlyOneTab); 6710 const bool isQtQuickControl(this->isQtQuickControl(option, widget)); 6711 bool isFirst(isSingle || position == QStyleOptionTab::Beginning); 6712 bool isLast(isSingle || position == QStyleOptionTab::End); 6713 bool isLeftOfSelected(!isLocked && tabOption->selectedPosition == QStyleOptionTab::NextIsSelected); 6714 bool isRightOfSelected(!isLocked && tabOption->selectedPosition == QStyleOptionTab::PreviousIsSelected); 6715 6716 // true if widget is aligned to the frame 6717 // need to check for 'isRightOfSelected' because for some reason the isFirst flag is set when active tab is being moved 6718 isFirst &= !isRightOfSelected; 6719 isLast &= !isLeftOfSelected; 6720 6721 // swap state based on reverse layout, so that they become layout independent 6722 const bool reverseLayout(option->direction == Qt::RightToLeft); 6723 const bool verticalTabs(isVerticalTab(tabOption)); 6724 if (reverseLayout && !verticalTabs) { 6725 qSwap(isFirst, isLast); 6726 qSwap(isLeftOfSelected, isRightOfSelected); 6727 } 6728 6729 // overlap 6730 // for QtQuickControls, ovelap is already accounted of in the option. Unlike in the qwidget case 6731 const int overlap = isQtQuickControl ? 0 : Metrics::TabBar_TabOverlap; 6732 6733 // adjust rect and define corners based on tabbar orientation 6734 Corners corners; 6735 switch (tabOption->shape) { 6736 case QTabBar::RoundedNorth: 6737 case QTabBar::TriangularNorth: 6738 if (selected) { 6739 corners = CornersTop; 6740 } else { 6741 if (isFirst) { 6742 corners |= CornerTopLeft; 6743 } 6744 if (isLast) { 6745 corners |= CornerTopRight; 6746 } 6747 if (isRightOfSelected) { 6748 rect.adjust(-Metrics::Frame_FrameRadius, 0, 0, 0); 6749 } 6750 if (isLeftOfSelected) { 6751 rect.adjust(0, 0, Metrics::Frame_FrameRadius, 0); 6752 } else if (!isLast) { 6753 rect.adjust(0, 0, overlap, 0); 6754 } 6755 } 6756 break; 6757 6758 case QTabBar::RoundedSouth: 6759 case QTabBar::TriangularSouth: 6760 if (selected) { 6761 corners = CornersBottom; 6762 } else { 6763 if (isFirst) { 6764 corners |= CornerBottomLeft; 6765 } 6766 if (isLast) { 6767 corners |= CornerBottomRight; 6768 } 6769 if (isRightOfSelected) { 6770 rect.adjust(-Metrics::Frame_FrameRadius, 0, 0, 0); 6771 } 6772 if (isLeftOfSelected) { 6773 rect.adjust(0, 0, Metrics::Frame_FrameRadius, 0); 6774 } else if (!isLast) { 6775 rect.adjust(0, 0, overlap, 0); 6776 } 6777 } 6778 break; 6779 6780 case QTabBar::RoundedWest: 6781 case QTabBar::TriangularWest: 6782 if (selected) { 6783 corners = CornersLeft; 6784 } else { 6785 if (isFirst) { 6786 corners |= CornerTopLeft; 6787 } 6788 if (isLast) { 6789 corners |= CornerBottomLeft; 6790 } 6791 if (isRightOfSelected) { 6792 rect.adjust(0, -Metrics::Frame_FrameRadius, 0, 0); 6793 } 6794 if (isLeftOfSelected) { 6795 rect.adjust(0, 0, 0, Metrics::Frame_FrameRadius); 6796 } else if (!isLast) { 6797 rect.adjust(0, 0, 0, overlap); 6798 } 6799 } 6800 break; 6801 6802 case QTabBar::RoundedEast: 6803 case QTabBar::TriangularEast: 6804 if (selected) { 6805 corners = CornersRight; 6806 } else { 6807 if (isFirst) { 6808 corners |= CornerTopRight; 6809 } 6810 if (isLast) { 6811 corners |= CornerBottomRight; 6812 } 6813 if (isRightOfSelected) { 6814 rect.adjust(0, -Metrics::Frame_FrameRadius, 0, 0); 6815 } 6816 if (isLeftOfSelected) { 6817 rect.adjust(0, 0, 0, Metrics::Frame_FrameRadius); 6818 } else if (!isLast) { 6819 rect.adjust(0, 0, 0, overlap); 6820 } 6821 } 6822 break; 6823 6824 default: 6825 break; 6826 } 6827 switch (tabOption->shape) { 6828 case QTabBar::RoundedNorth: 6829 case QTabBar::TriangularNorth: 6830 rect.adjust(0, 0, 0, 1); 6831 break; 6832 case QTabBar::RoundedSouth: 6833 case QTabBar::TriangularSouth: 6834 rect.adjust(0, 0, 0, -1); 6835 break; 6836 case QTabBar::RoundedWest: 6837 case QTabBar::TriangularWest: 6838 rect.adjust(0, 0, 1, 0); 6839 break; 6840 case QTabBar::RoundedEast: 6841 case QTabBar::TriangularEast: 6842 rect.adjust(0, 0, -1, 0); 6843 break; 6844 } 6845 6846 QHash<QByteArray, bool> stateProperties; 6847 stateProperties["enabled"] = enabled; 6848 stateProperties["visualFocus"] = visualFocus; 6849 stateProperties["hovered"] = hovered; 6850 stateProperties["down"] = down; 6851 stateProperties["selected"] = selected; 6852 stateProperties["documentMode"] = true; 6853 stateProperties["north"] = north; 6854 stateProperties["south"] = south; 6855 stateProperties["west"] = west; 6856 stateProperties["east"] = east; 6857 stateProperties["isQtQuickControl"] = isQtQuickControl; 6858 stateProperties["hasAlteredBackground"] = hasAlteredBackground(widget); 6859 _helper->renderTabBarTab(painter, rect, option->palette, stateProperties, corners, animation); 6860 6861 return true; 6862 } 6863 6864 //___________________________________________________________________________________ 6865 bool Style::drawToolBoxTabLabelControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6866 { 6867 // rendering is similar to drawPushButtonLabelControl 6868 // cast option and check 6869 const auto toolBoxOption(qstyleoption_cast<const QStyleOptionToolBox *>(option)); 6870 if (!toolBoxOption) { 6871 return true; 6872 } 6873 6874 // copy palette 6875 const auto &palette(option->palette); 6876 6877 const State &state(option->state); 6878 const bool enabled(state & State_Enabled); 6879 6880 // text alignment 6881 const int textFlags(_mnemonics->textFlags() | Qt::AlignCenter); 6882 6883 // contents rect 6884 const auto rect(subElementRect(SE_ToolBoxTabContents, option, widget)); 6885 6886 // store icon size 6887 const int iconSize(pixelMetric(QStyle::PM_SmallIconSize, option, widget)); 6888 6889 // find contents size and rect 6890 auto contentsRect(rect); 6891 QSize contentsSize; 6892 if (!toolBoxOption->text.isEmpty()) { 6893 contentsSize = option->fontMetrics.size(_mnemonics->textFlags(), toolBoxOption->text); 6894 if (!toolBoxOption->icon.isNull()) { 6895 contentsSize.rwidth() += Metrics::ToolBox_TabItemSpacing; 6896 } 6897 } 6898 6899 // icon size 6900 if (!toolBoxOption->icon.isNull()) { 6901 contentsSize.setHeight(qMax(contentsSize.height(), iconSize)); 6902 contentsSize.rwidth() += iconSize; 6903 } 6904 6905 // adjust contents rect 6906 contentsRect = centerRect(contentsRect, contentsSize); 6907 6908 // render icon 6909 if (!toolBoxOption->icon.isNull()) { 6910 // icon rect 6911 QRect iconRect; 6912 if (toolBoxOption->text.isEmpty()) { 6913 iconRect = centerRect(contentsRect, iconSize, iconSize); 6914 } else { 6915 iconRect = contentsRect; 6916 iconRect.setWidth(iconSize); 6917 iconRect = centerRect(iconRect, iconSize, iconSize); 6918 contentsRect.setLeft(iconRect.right() + Metrics::ToolBox_TabItemSpacing + 1); 6919 } 6920 6921 iconRect = visualRect(option, iconRect); 6922 const QIcon::Mode mode(enabled ? QIcon::Normal : QIcon::Disabled); 6923 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 6924 const QPixmap pixmap(_helper->coloredIcon(toolBoxOption->icon, toolBoxOption->palette, iconRect.size(), dpr, mode)); 6925 drawItemPixmap(painter, iconRect, textFlags, pixmap); 6926 } 6927 6928 // render text 6929 if (!toolBoxOption->text.isEmpty()) { 6930 contentsRect = visualRect(option, contentsRect); 6931 drawItemText(painter, contentsRect, textFlags, palette, enabled, toolBoxOption->text, QPalette::WindowText); 6932 } 6933 6934 return true; 6935 } 6936 6937 //___________________________________________________________________________________ 6938 bool Style::drawToolBoxTabShapeControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6939 { 6940 // cast option and check 6941 const auto toolBoxOption(qstyleoption_cast<const QStyleOptionToolBox *>(option)); 6942 if (!toolBoxOption) { 6943 return true; 6944 } 6945 6946 // copy rect and palette 6947 const auto &rect(option->rect); 6948 const auto tabRect(toolBoxTabContentsRect(option, widget)); 6949 6950 /* 6951 * important: option returns the wrong palette. 6952 * we use the widget palette instead, when set 6953 */ 6954 const auto &palette(widget ? widget->palette() : option->palette); 6955 6956 // store flags 6957 const State &flags(option->state); 6958 const bool enabled(flags & State_Enabled); 6959 const bool selected(flags & State_Selected); 6960 const bool mouseOver(enabled && !selected && (flags & State_MouseOver)); 6961 6962 // update animation state 6963 /* 6964 * the proper widget ( the toolbox tab ) is not passed as argument by Qt. 6965 * What is passed is the toolbox directly. To implement animations properly, 6966 *the painter->device() is used instead 6967 */ 6968 bool isAnimated(false); 6969 qreal opacity(AnimationData::OpacityInvalid); 6970 QPaintDevice *device = painter->device(); 6971 if (enabled && device) { 6972 _animations->toolBoxEngine().updateState(device, mouseOver); 6973 isAnimated = _animations->toolBoxEngine().isAnimated(device); 6974 opacity = _animations->toolBoxEngine().opacity(device); 6975 } 6976 6977 // color 6978 QColor outline; 6979 if (selected) { 6980 outline = _helper->focusColor(palette); 6981 } else { 6982 outline = _helper->frameOutlineColor(palette, mouseOver, false, opacity, isAnimated ? AnimationHover : AnimationNone); 6983 } 6984 6985 // render 6986 _helper->renderToolBoxFrame(painter, rect, tabRect.width(), outline); 6987 6988 return true; 6989 } 6990 6991 //___________________________________________________________________________________ 6992 bool Style::drawDockWidgetTitleControl(const QStyleOption *option, QPainter *painter, const QWidget *widget) const 6993 { 6994 // cast option and check 6995 const auto dockWidgetOption = qstyleoption_cast<const QStyleOptionDockWidget *>(option); 6996 if (!dockWidgetOption) { 6997 return true; 6998 } 6999 7000 const auto &palette(option->palette); 7001 const auto &state(option->state); 7002 const bool enabled(state & State_Enabled); 7003 const bool reverseLayout(option->direction == Qt::RightToLeft); 7004 7005 // cast to v2 to check vertical bar 7006 const bool verticalTitleBar(dockWidgetOption->verticalTitleBar); 7007 7008 const auto buttonRect(subElementRect(dockWidgetOption->floatable ? SE_DockWidgetFloatButton : SE_DockWidgetCloseButton, option, widget)); 7009 7010 // get rectangle and adjust to properly accounts for buttons 7011 auto rect(insideMargin(dockWidgetOption->rect, Metrics::Frame_FrameWidth)); 7012 if (verticalTitleBar) { 7013 if (buttonRect.isValid()) { 7014 rect.setTop(buttonRect.bottom() + 1); 7015 } 7016 7017 } else if (reverseLayout) { 7018 if (buttonRect.isValid()) { 7019 rect.setLeft(buttonRect.right() + 1); 7020 } 7021 rect.adjust(0, 0, -4, 0); 7022 7023 } else { 7024 if (buttonRect.isValid()) { 7025 rect.setRight(buttonRect.left() - 1); 7026 } 7027 rect.adjust(4, 0, 0, 0); 7028 } 7029 7030 if (!verticalTitleBar) { 7031 auto rect(option->rect); 7032 rect.setY(rect.height() - 1); 7033 rect.setHeight(1); 7034 auto palette = option->palette; 7035 palette.setCurrentColorGroup(QPalette::Disabled); 7036 const auto color(_helper->separatorColor(palette)); 7037 _helper->renderSeparator(painter, rect, color, false); 7038 } 7039 7040 QString title(dockWidgetOption->title); 7041 int titleWidth = dockWidgetOption->fontMetrics.size(_mnemonics->textFlags(), title).width(); 7042 int width = verticalTitleBar ? rect.height() : rect.width(); 7043 if (width < titleWidth) { 7044 title = dockWidgetOption->fontMetrics.elidedText(title, Qt::ElideRight, width, Qt::TextShowMnemonic); 7045 } 7046 7047 if (verticalTitleBar) { 7048 QSize size = rect.size(); 7049 size.transpose(); 7050 rect.setSize(size); 7051 7052 painter->save(); 7053 painter->translate(rect.left(), rect.top() + rect.width()); 7054 painter->rotate(-90); 7055 painter->translate(-rect.left(), -rect.top()); 7056 drawItemText(painter, rect, Qt::AlignLeft | Qt::AlignVCenter | _mnemonics->textFlags(), palette, enabled, title, QPalette::WindowText); 7057 painter->restore(); 7058 7059 } else { 7060 drawItemText(painter, rect, Qt::AlignLeft | Qt::AlignVCenter | _mnemonics->textFlags(), palette, enabled, title, QPalette::WindowText); 7061 } 7062 7063 return true; 7064 } 7065 7066 //______________________________________________________________ 7067 bool Style::drawSplitterControl(const QStyleOption *option, QPainter *painter, [[maybe_unused]] const QWidget *widget) const 7068 { 7069 painter->setBrush(_helper->separatorColor(option->palette)); 7070 painter->setPen(Qt::NoPen); 7071 painter->drawRect(option->rect); 7072 return true; 7073 } 7074 //______________________________________________________________ 7075 bool Style::drawGroupBoxComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7076 { 7077 // base class method 7078 ParentStyleClass::drawComplexControl(CC_GroupBox, option, painter, widget); 7079 7080 // cast option and check 7081 const auto groupBoxOption = qstyleoption_cast<const QStyleOptionGroupBox *>(option); 7082 if (!groupBoxOption) { 7083 return true; 7084 } 7085 7086 // do nothing if either label is not selected or groupbox is empty 7087 if (!(option->subControls & QStyle::SC_GroupBoxLabel) || groupBoxOption->text.isEmpty()) { 7088 return true; 7089 } 7090 7091 // store palette and rect 7092 const auto &palette(option->palette); 7093 7094 // check focus state 7095 const State &state(option->state); 7096 const bool enabled(state & State_Enabled); 7097 const bool hasFocus(enabled && (option->state & State_HasFocus)); 7098 if (!hasFocus) { 7099 return true; 7100 } 7101 7102 // alignment 7103 const int textFlags(groupBoxOption->textAlignment | _mnemonics->textFlags()); 7104 7105 // update animation state 7106 _animations->widgetStateEngine().updateState(widget, AnimationFocus, hasFocus); 7107 const bool isFocusAnimated(_animations->widgetStateEngine().isAnimated(widget, AnimationFocus)); 7108 const qreal opacity(_animations->widgetStateEngine().opacity(widget, AnimationFocus)); 7109 7110 // get relevant rect 7111 auto textRect = subControlRect(CC_GroupBox, option, SC_GroupBoxLabel, widget); 7112 textRect = option->fontMetrics.boundingRect(textRect, textFlags, groupBoxOption->text); 7113 7114 // focus color 7115 QColor focusColor; 7116 if (isFocusAnimated) { 7117 focusColor = _helper->alphaColor(_helper->focusColor(palette), opacity); 7118 } else if (hasFocus) { 7119 focusColor = _helper->focusColor(palette); 7120 } 7121 7122 // render focus 7123 _helper->renderFocusLine(painter, textRect, focusColor); 7124 7125 return true; 7126 } 7127 7128 //______________________________________________________________ 7129 bool Style::drawToolButtonComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7130 { 7131 // cast option and check 7132 const auto toolButtonOption(qstyleoption_cast<const QStyleOptionToolButton *>(option)); 7133 if (!toolButtonOption) { 7134 return true; 7135 } 7136 7137 // need to alter palette for focused buttons 7138 const bool activeFocus = option->state & QStyle::State_HasFocus; 7139 const bool hovered = option->state & QStyle::State_MouseOver; 7140 bool flat = option->state & QStyle::State_AutoRaise; 7141 7142 // update animation state 7143 // mouse over takes precedence over focus 7144 _animations->widgetStateEngine().updateState(widget, AnimationHover, hovered); 7145 _animations->widgetStateEngine().updateState(widget, AnimationFocus, activeFocus && !hovered); 7146 7147 // detect buttons in tabbar, for which special rendering is needed 7148 const bool inTabBar(widget && qobject_cast<const QTabBar *>(widget->parentWidget())); 7149 7150 // copy option and alter palette 7151 QStyleOptionToolButton copy(*toolButtonOption); 7152 7153 const auto menuStyle = BreezePrivate::toolButtonMenuArrowStyle(option); 7154 7155 const auto buttonRect(subControlRect(CC_ToolButton, option, SC_ToolButton, widget)); 7156 const auto menuRect(subControlRect(CC_ToolButton, option, SC_ToolButtonMenu, widget)); 7157 7158 // frame 7159 if (toolButtonOption->subControls & SC_ToolButton) { 7160 if (!flat) { 7161 copy.rect = buttonRect; 7162 } 7163 if (inTabBar) { 7164 drawTabBarPanelButtonToolPrimitive(©, painter, widget); 7165 } else { 7166 drawPrimitive(PE_PanelButtonTool, ©, painter, widget); 7167 } 7168 } 7169 7170 // arrow 7171 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::SubControl) { 7172 copy.rect = menuRect; 7173 drawPrimitive(PE_IndicatorButtonDropDown, ©, painter, widget); 7174 7175 copy.state &= ~State_MouseOver; 7176 copy.state &= ~State_Sunken; 7177 copy.state &= ~State_On; 7178 drawPrimitive(PE_IndicatorArrowDown, ©, painter, widget); 7179 7180 } else if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::InlineSmall || menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::InlineLarge) { 7181 copy.rect = menuRect; 7182 copy.state &= ~State_MouseOver; 7183 copy.state &= ~State_Sunken; 7184 copy.state &= ~State_On; 7185 7186 if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::InlineSmall) { 7187 drawIndicatorArrowPrimitive(ArrowDown, ©, painter, widget); 7188 } else { 7189 if (option->direction == Qt::RightToLeft) { 7190 copy.rect.translate(Metrics::Button_ItemSpacing, 0); 7191 } else { 7192 copy.rect.translate(-Metrics::Button_ItemSpacing, 0); 7193 } 7194 drawIndicatorArrowPrimitive(ArrowDown, ©, painter, widget); 7195 } 7196 } 7197 7198 // contents 7199 { 7200 // restore state 7201 copy.state = option->state; 7202 7203 // define contents rect 7204 auto contentsRect(buttonRect); 7205 7206 // detect dock widget title button 7207 // for dockwidget title buttons, do not take out margins, so that icon do not get scaled down 7208 const bool isDockWidgetTitleButton(widget && widget->inherits("QDockWidgetTitleButton")); 7209 if (isDockWidgetTitleButton) { 7210 // cast to abstract button 7211 // adjust state to have correct icon rendered 7212 const auto button(qobject_cast<const QAbstractButton *>(widget)); 7213 if (button->isChecked() || button->isDown()) { 7214 copy.state |= State_On; 7215 } 7216 } 7217 7218 copy.rect = contentsRect; 7219 7220 // render 7221 drawControl(CE_ToolButtonLabel, ©, painter, widget); 7222 } 7223 7224 return true; 7225 } 7226 7227 //______________________________________________________________ 7228 bool Style::drawComboBoxComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7229 { 7230 // state 7231 bool enabled = option->state & QStyle::State_Enabled; 7232 bool activeFocus = option->state & QStyle::State_HasFocus; 7233 bool visualFocus = activeFocus && option->state & QStyle::State_KeyboardFocusChange && (widget == nullptr || widget->focusProxy() == nullptr); 7234 bool hovered = option->state & QStyle::State_MouseOver; 7235 // Only true if the arrow (SC_ComboBoxArrow) is pressed 7236 bool down = option->state & QStyle::State_Sunken; 7237 // Only true if the background (QComboBoxPrivateContainer) is pressed 7238 bool checked = option->state & QStyle::State_On; 7239 bool flat = false; 7240 bool editable = false; 7241 bool hasNeutralHighlight = hasHighlightNeutral(widget, option); 7242 7243 // cast option and check 7244 const auto comboBoxOption = qstyleoption_cast<const QStyleOptionComboBox *>(option); 7245 if (comboBoxOption) { 7246 flat = !comboBoxOption->frame; 7247 editable = comboBoxOption->editable; 7248 } 7249 7250 // frame 7251 if (option->subControls & SC_ComboBoxFrame) { 7252 if (editable) { 7253 flat |= (option->rect.height() <= 2 * Metrics::Frame_FrameWidth + Metrics::MenuButton_IndicatorWidth); 7254 if (flat) { 7255 const auto &background = option->palette.color(QPalette::Base); 7256 painter->setBrush(background); 7257 painter->setPen(Qt::NoPen); 7258 painter->drawRect(option->rect); 7259 } else { 7260 drawPrimitive(PE_FrameLineEdit, option, painter, widget); 7261 } 7262 } else { 7263 // NOTE: Using focus animation for bg down because the pressed animation only works on press when enabled for buttons and not on release. 7264 _animations->widgetStateEngine().updateState(widget, AnimationFocus, (down || checked) && enabled); 7265 // NOTE: Using hover animation for all pen animations to prevent flickering when closing the menu. 7266 _animations->widgetStateEngine().updateState(widget, AnimationHover, (hovered || visualFocus || down || checked) && enabled); 7267 qreal bgAnimation = _animations->widgetStateEngine().opacity(widget, AnimationFocus); 7268 qreal penAnimation = _animations->widgetStateEngine().opacity(widget, AnimationHover); 7269 7270 QHash<QByteArray, bool> stateProperties; 7271 stateProperties["enabled"] = enabled; 7272 stateProperties["visualFocus"] = visualFocus; 7273 stateProperties["hovered"] = hovered; 7274 // See notes for down and checked above. 7275 stateProperties["down"] = down || checked; 7276 stateProperties["flat"] = flat; 7277 stateProperties["hasNeutralHighlight"] = hasNeutralHighlight; 7278 stateProperties["isActiveWindow"] = widget ? widget->isActiveWindow() : true; 7279 7280 _helper->renderButtonFrame(painter, option->rect, option->palette, stateProperties, bgAnimation, penAnimation); 7281 } 7282 } 7283 7284 // arrow 7285 if (option->subControls & SC_ComboBoxArrow) { 7286 // detect empty comboboxes 7287 const auto comboBox = qobject_cast<const QComboBox *>(widget); 7288 const bool empty(comboBox && !comboBox->count()); 7289 7290 // arrow color 7291 QColor arrowColor; 7292 if (editable) { 7293 if (empty || !enabled) { 7294 arrowColor = option->palette.color(QPalette::Disabled, QPalette::Text); 7295 } else { 7296 // check animation state 7297 const bool subControlHover(enabled && hovered && option->activeSubControls & SC_ComboBoxArrow); 7298 _animations->comboBoxEngine().updateState(widget, AnimationHover, subControlHover); 7299 7300 const bool animated(enabled && _animations->comboBoxEngine().isAnimated(widget, AnimationHover)); 7301 const qreal opacity(_animations->comboBoxEngine().opacity(widget, AnimationHover)); 7302 7303 // color 7304 const auto normal(_helper->arrowColor(option->palette, QPalette::WindowText)); 7305 const auto hover(_helper->hoverColor(option->palette)); 7306 7307 if (animated) { 7308 arrowColor = KColorUtils::mix(normal, hover, opacity); 7309 } else if (subControlHover) { 7310 arrowColor = hover; 7311 } else { 7312 arrowColor = normal; 7313 } 7314 } 7315 } else if (flat) { 7316 if (empty || !enabled) { 7317 arrowColor = _helper->arrowColor(option->palette, QPalette::Disabled, QPalette::WindowText); 7318 } else if (activeFocus && !hovered && down) { 7319 arrowColor = option->palette.color(QPalette::WindowText); 7320 } else { 7321 arrowColor = _helper->arrowColor(option->palette, QPalette::WindowText); 7322 } 7323 } else if (empty || !enabled) { 7324 arrowColor = _helper->arrowColor(option->palette, QPalette::Disabled, QPalette::ButtonText); 7325 } else if (activeFocus && !hovered) { 7326 arrowColor = option->palette.color(QPalette::WindowText); 7327 } else { 7328 arrowColor = _helper->arrowColor(option->palette, QPalette::ButtonText); 7329 } 7330 7331 // arrow rect 7332 auto arrowRect(subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget)); 7333 7334 // render 7335 _helper->renderArrow(painter, arrowRect, arrowColor, ArrowDown); 7336 } 7337 return true; 7338 } 7339 7340 //______________________________________________________________ 7341 bool Style::drawSpinBoxComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7342 { 7343 const auto spinBoxOption(qstyleoption_cast<const QStyleOptionSpinBox *>(option)); 7344 if (!spinBoxOption) { 7345 return true; 7346 } 7347 7348 // store palette and rect 7349 const auto &palette(option->palette); 7350 const auto &rect(option->rect); 7351 7352 if (option->subControls & SC_SpinBoxFrame) { 7353 // detect flat spinboxes 7354 bool flat(!spinBoxOption->frame); 7355 flat |= (rect.height() < 2 * Metrics::Frame_FrameWidth + Metrics::SpinBox_ArrowButtonWidth); 7356 if (flat) { 7357 const auto &background = palette.color(QPalette::Base); 7358 7359 painter->setBrush(background); 7360 painter->setPen(Qt::NoPen); 7361 painter->drawRect(rect); 7362 7363 } else { 7364 drawPrimitive(PE_FrameLineEdit, option, painter, widget); 7365 } 7366 } 7367 7368 if (option->subControls & SC_SpinBoxUp) { 7369 renderSpinBoxArrow(SC_SpinBoxUp, spinBoxOption, painter, widget); 7370 } 7371 if (option->subControls & SC_SpinBoxDown) { 7372 renderSpinBoxArrow(SC_SpinBoxDown, spinBoxOption, painter, widget); 7373 } 7374 7375 return true; 7376 } 7377 7378 //______________________________________________________________ 7379 bool Style::drawSliderComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7380 { 7381 // cast option and check 7382 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 7383 if (!sliderOption) { 7384 return true; 7385 } 7386 7387 // copy rect and palette 7388 const auto &rect(option->rect); 7389 const auto &palette(option->palette); 7390 7391 // copy state 7392 const State &state(option->state); 7393 const bool enabled(state & State_Enabled); 7394 const bool mouseOver(enabled && (state & State_MouseOver)); 7395 const bool hasFocus(enabled && (state & State_HasFocus)); 7396 7397 // direction 7398 const bool horizontal(sliderOption->orientation == Qt::Horizontal); 7399 7400 // retrieve rects of sub controls 7401 const auto grooveRect(subControlRect(CC_Slider, sliderOption, SC_SliderGroove, widget)); 7402 const auto handleRect(subControlRect(CC_Slider, sliderOption, SC_SliderHandle, widget)); 7403 7404 // tickmarks 7405 if (StyleConfigData::sliderDrawTickMarks() && (sliderOption->subControls & SC_SliderTickmarks)) { 7406 const bool upsideDown(sliderOption->upsideDown); 7407 const int tickPosition(sliderOption->tickPosition); 7408 const int available(pixelMetric(PM_SliderSpaceAvailable, option, widget)); 7409 int interval = sliderOption->tickInterval; 7410 if (interval < 1) { 7411 interval = sliderOption->pageStep; 7412 } 7413 if (interval >= 1) { 7414 const int fudge(pixelMetric(PM_SliderLength, option, widget) / 2); 7415 int current(sliderOption->minimum); 7416 7417 // store tick lines 7418 QList<QLine> tickLines; 7419 if (horizontal) { 7420 if (tickPosition & QSlider::TicksAbove) { 7421 tickLines.append(QLine(rect.left(), 7422 grooveRect.top() - Metrics::Slider_TickMarginWidth, 7423 rect.left(), 7424 grooveRect.top() - Metrics::Slider_TickMarginWidth - Metrics::Slider_TickLength)); 7425 } 7426 if (tickPosition & QSlider::TicksBelow) { 7427 tickLines.append(QLine(rect.left(), 7428 grooveRect.bottom() + Metrics::Slider_TickMarginWidth, 7429 rect.left(), 7430 grooveRect.bottom() + Metrics::Slider_TickMarginWidth + Metrics::Slider_TickLength)); 7431 } 7432 7433 } else { 7434 if (tickPosition & QSlider::TicksAbove) { 7435 tickLines.append(QLine(grooveRect.left() - Metrics::Slider_TickMarginWidth, 7436 rect.top(), 7437 grooveRect.left() - Metrics::Slider_TickMarginWidth - Metrics::Slider_TickLength, 7438 rect.top())); 7439 } 7440 if (tickPosition & QSlider::TicksBelow) { 7441 tickLines.append(QLine(grooveRect.right() + Metrics::Slider_TickMarginWidth, 7442 rect.top(), 7443 grooveRect.right() + Metrics::Slider_TickMarginWidth + Metrics::Slider_TickLength, 7444 rect.top())); 7445 } 7446 } 7447 7448 // colors 7449 const auto reverse(option->direction == Qt::RightToLeft); 7450 const auto base(_helper->separatorColor(palette)); 7451 const auto &highlight = 7452 hasHighlightNeutral(widget, option, mouseOver, hasFocus) ? _helper->neutralText(palette) : palette.color(QPalette::Highlight); 7453 7454 while (current <= sliderOption->maximum) { 7455 // adjust color 7456 const auto color((enabled && current <= sliderOption->sliderPosition) ? highlight : base); 7457 painter->setPen(color); 7458 7459 // calculate positions and draw lines 7460 const int position(sliderPositionFromValue(sliderOption->minimum, sliderOption->maximum, current, available, upsideDown) + fudge); 7461 for (const QLine &tickLine : std::as_const(tickLines)) { 7462 if (horizontal) { 7463 painter->drawLine(tickLine.translated(reverse ? (rect.width() - position) : position, 0)); 7464 } else { 7465 painter->drawLine(tickLine.translated(0, position)); 7466 } 7467 } 7468 7469 // go to next position 7470 current += interval; 7471 } 7472 } 7473 } 7474 7475 // groove 7476 if (sliderOption->subControls & SC_SliderGroove) { 7477 // base color 7478 const auto grooveColor(_helper->alphaColor(palette.color(QPalette::WindowText), 0.2)); 7479 7480 if (!enabled) { 7481 _helper->renderSliderGroove(painter, grooveRect, grooveColor, palette.color(QPalette::Window)); 7482 } else { 7483 const bool upsideDown(sliderOption->upsideDown); 7484 7485 // highlight color 7486 const auto &highlight = 7487 hasHighlightNeutral(widget, option, mouseOver, hasFocus) ? _helper->neutralText(palette) : palette.color(QPalette::Highlight); 7488 7489 if (sliderOption->orientation == Qt::Horizontal) { 7490 auto leftRect(grooveRect); 7491 leftRect.setRight(handleRect.right() - Metrics::Slider_ControlThickness / 2); 7492 7493 auto rightRect(grooveRect); 7494 rightRect.setLeft(handleRect.left() + Metrics::Slider_ControlThickness / 2); 7495 7496 if (option->direction == Qt::RightToLeft) { 7497 std::swap(leftRect, rightRect); 7498 } 7499 7500 // Background 7501 _helper->renderSliderGroove(painter, leftRect.united(rightRect), grooveColor, palette.color(QPalette::Window)); 7502 // Fill 7503 _helper->renderSliderGroove(painter, upsideDown ? rightRect : leftRect, highlight, palette.color(QPalette::Window)); 7504 7505 } else { 7506 auto topRect(grooveRect); 7507 auto bottomRect(grooveRect); 7508 topRect.setBottom(handleRect.bottom() - Metrics::Slider_ControlThickness / 2); 7509 bottomRect.setTop(handleRect.top() + Metrics::Slider_ControlThickness / 2); 7510 7511 // Background 7512 _helper->renderSliderGroove(painter, topRect.united(bottomRect), grooveColor, palette.color(QPalette::Window)); 7513 // Fill 7514 _helper->renderSliderGroove(painter, upsideDown ? bottomRect : topRect, highlight, palette.color(QPalette::Window)); 7515 } 7516 } 7517 } 7518 7519 // handle 7520 if (sliderOption->subControls & SC_SliderHandle) { 7521 // handle state 7522 const bool handleActive(sliderOption->activeSubControls & SC_SliderHandle); 7523 const bool sunken(state & (State_On | State_Sunken)); 7524 7525 // animation state 7526 _animations->widgetStateEngine().updateState(widget, AnimationHover, handleActive && mouseOver); 7527 _animations->widgetStateEngine().updateState(widget, AnimationFocus, hasFocus); 7528 const AnimationMode mode(_animations->widgetStateEngine().buttonAnimationMode(widget)); 7529 const qreal opacity(_animations->widgetStateEngine().buttonOpacity(widget)); 7530 7531 // define colors 7532 const auto &background = palette.color(QPalette::Button); 7533 auto outline(_helper->sliderOutlineColor(palette, handleActive && mouseOver, hasFocus, opacity, mode)); 7534 if (hasFocus || (handleActive && mouseOver)) { 7535 outline = hasHighlightNeutral(widget, option, handleActive && mouseOver, hasFocus) 7536 ? _helper->neutralText(palette).lighter(mouseOver || hasFocus ? 150 : 100) 7537 : outline; 7538 } 7539 const auto shadow(_helper->shadowColor(palette)); 7540 7541 // render 7542 _helper->renderSliderHandle(painter, handleRect, background, outline, shadow, sunken); 7543 } 7544 7545 return true; 7546 } 7547 7548 //______________________________________________________________ 7549 bool Style::drawDialComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7550 { 7551 // cast option and check 7552 const auto sliderOption(qstyleoption_cast<const QStyleOptionSlider *>(option)); 7553 if (!sliderOption) { 7554 return true; 7555 } 7556 7557 const auto &palette(option->palette); 7558 const State &state(option->state); 7559 const bool enabled(state & State_Enabled); 7560 const bool mouseOver(enabled && (state & State_MouseOver)); 7561 const bool hasFocus(enabled && (state & State_HasFocus)); 7562 7563 // do not render tickmarks 7564 if (sliderOption->subControls & SC_DialTickmarks) { } 7565 7566 // groove 7567 if (sliderOption->subControls & SC_DialGroove) { 7568 // groove rect 7569 auto grooveRect(subControlRect(CC_Dial, sliderOption, SC_SliderGroove, widget)); 7570 7571 // groove 7572 const auto grooveColor(KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.2)); 7573 7574 // angles 7575 const qreal first(dialAngle(sliderOption, sliderOption->minimum)); 7576 const qreal last(dialAngle(sliderOption, sliderOption->maximum)); 7577 7578 // render groove 7579 _helper->renderDialGroove(painter, grooveRect, grooveColor, palette.color(QPalette::Window), first, last); 7580 7581 if (enabled) { 7582 // highlight 7583 const auto &highlight = palette.color(QPalette::Highlight); 7584 7585 // angles 7586 const qreal second(dialAngle(sliderOption, sliderOption->sliderPosition)); 7587 7588 // render contents 7589 _helper->renderDialGroove(painter, grooveRect, highlight, palette.color(QPalette::Window), first, second); 7590 } 7591 } 7592 7593 // handle 7594 if (sliderOption->subControls & SC_DialHandle) { 7595 // get handle rect 7596 auto handleRect(subControlRect(CC_Dial, sliderOption, SC_DialHandle, widget)); 7597 handleRect = centerRect(handleRect, Metrics::Slider_ControlThickness, Metrics::Slider_ControlThickness); 7598 7599 // handle state 7600 const bool handleActive(mouseOver && handleRect.contains(_animations->dialEngine().position(widget))); 7601 const bool sunken(state & (State_On | State_Sunken)); 7602 7603 // animation state 7604 _animations->dialEngine().setHandleRect(widget, handleRect); 7605 _animations->dialEngine().updateState(widget, AnimationHover, handleActive && mouseOver); 7606 _animations->dialEngine().updateState(widget, AnimationFocus, hasFocus); 7607 const auto mode(_animations->dialEngine().buttonAnimationMode(widget)); 7608 const qreal opacity(_animations->dialEngine().buttonOpacity(widget)); 7609 7610 // define colors 7611 const auto &background = palette.color(QPalette::Button); 7612 const auto outline(_helper->sliderOutlineColor(palette, handleActive && mouseOver, hasFocus, opacity, mode)); 7613 const auto shadow(_helper->shadowColor(palette)); 7614 7615 // render 7616 _helper->renderSliderHandle(painter, handleRect, background, outline, shadow, sunken); 7617 } 7618 7619 return true; 7620 } 7621 7622 //______________________________________________________________ 7623 bool Style::drawScrollBarComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7624 { 7625 QRect separatorRect; 7626 if (option->state & State_Horizontal) { 7627 separatorRect = QRect(0, 0, option->rect.width(), PenWidth::Frame); 7628 } else { 7629 separatorRect = alignedRect(option->direction, Qt::AlignLeft, QSize(PenWidth::Frame, option->rect.height()), option->rect); 7630 } 7631 7632 _helper->renderScrollBarBorder(painter, separatorRect, _helper->alphaColor(option->palette.color(QPalette::Text), Metrics::Bias_Default)); 7633 7634 // call base class primitive 7635 ParentStyleClass::drawComplexControl(CC_ScrollBar, option, painter, widget); 7636 7637 return true; 7638 } 7639 7640 //______________________________________________________________ 7641 bool Style::drawTitleBarComplexControl(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const 7642 { 7643 // cast option and check 7644 const auto titleBarOption(qstyleoption_cast<const QStyleOptionTitleBar *>(option)); 7645 if (!titleBarOption) { 7646 return true; 7647 } 7648 7649 // store palette and rect 7650 auto palette(option->palette); 7651 const auto &rect(option->rect); 7652 7653 const State &flags(option->state); 7654 const bool enabled(flags & State_Enabled); 7655 const bool active(enabled && (titleBarOption->titleBarState & Qt::WindowActive)); 7656 7657 if (titleBarOption->subControls & SC_TitleBarLabel) { 7658 // render background 7659 painter->setClipRect(rect); 7660 const auto outline(active ? QColor() : _helper->frameOutlineColor(palette, false, false)); 7661 const auto background(_helper->titleBarColor(active)); 7662 _helper->renderTabWidgetFrame(painter, rect.adjusted(-1, -1, 1, 3), background, outline, CornersTop); 7663 7664 const bool useSeparator(active && _helper->titleBarColor(active) != palette.color(QPalette::Window) 7665 && !(titleBarOption->titleBarState & Qt::WindowMinimized)); 7666 7667 if (useSeparator) { 7668 painter->setRenderHint(QPainter::Antialiasing, false); 7669 painter->setBrush(Qt::NoBrush); 7670 painter->setPen(palette.color(QPalette::Highlight)); 7671 painter->drawLine(rect.bottomLeft(), rect.bottomRight()); 7672 } 7673 7674 // render text 7675 palette.setColor(QPalette::WindowText, _helper->titleBarTextColor(active)); 7676 const auto textRect(subControlRect(CC_TitleBar, option, SC_TitleBarLabel, widget)); 7677 ParentStyleClass::drawItemText(painter, textRect, Qt::AlignCenter, palette, active, titleBarOption->text, QPalette::WindowText); 7678 } 7679 7680 // buttons 7681 static const QList<SubControl> subControls = {SC_TitleBarMinButton, 7682 SC_TitleBarMaxButton, 7683 SC_TitleBarCloseButton, 7684 SC_TitleBarNormalButton, 7685 SC_TitleBarSysMenu}; 7686 7687 // loop over supported buttons 7688 for (const SubControl &subControl : subControls) { 7689 // skip if not requested 7690 if (!(titleBarOption->subControls & subControl)) { 7691 continue; 7692 } 7693 7694 // find matching icon 7695 QIcon icon; 7696 switch (subControl) { 7697 case SC_TitleBarMinButton: 7698 icon = standardIcon(SP_TitleBarMinButton, option, widget); 7699 break; 7700 case SC_TitleBarMaxButton: 7701 icon = standardIcon(SP_TitleBarMaxButton, option, widget); 7702 break; 7703 case SC_TitleBarCloseButton: 7704 icon = standardIcon(SP_TitleBarCloseButton, option, widget); 7705 break; 7706 case SC_TitleBarNormalButton: 7707 icon = standardIcon(SP_TitleBarNormalButton, option, widget); 7708 break; 7709 case SC_TitleBarSysMenu: 7710 icon = titleBarOption->icon; 7711 break; 7712 default: 7713 break; 7714 } 7715 7716 // check icon 7717 if (icon.isNull()) { 7718 continue; 7719 } 7720 7721 // define icon rect 7722 auto iconRect(subControlRect(CC_TitleBar, option, subControl, widget)); 7723 if (iconRect.isEmpty()) { 7724 continue; 7725 } 7726 7727 // active state 7728 const bool subControlActive(titleBarOption->activeSubControls & subControl); 7729 7730 // mouse over state 7731 const bool mouseOver(!subControlActive && widget && iconRect.translated(widget->mapToGlobal(QPoint(0, 0))).contains(QCursor::pos())); 7732 7733 // adjust iconRect 7734 const int iconWidth(pixelMetric(PM_SmallIconSize, option, widget)); 7735 const QSize iconSize(iconWidth, iconWidth); 7736 iconRect = centerRect(iconRect, iconSize); 7737 7738 // set icon mode and state 7739 QIcon::Mode iconMode; 7740 QIcon::State iconState; 7741 7742 if (!enabled) { 7743 iconMode = QIcon::Disabled; 7744 iconState = QIcon::Off; 7745 7746 } else { 7747 if (mouseOver) { 7748 iconMode = QIcon::Active; 7749 } else if (active) { 7750 iconMode = QIcon::Selected; 7751 } else { 7752 iconMode = QIcon::Normal; 7753 } 7754 7755 iconState = subControlActive ? QIcon::On : QIcon::Off; 7756 } 7757 7758 // get pixmap and render 7759 const qreal dpr = painter->device() ? painter->device()->devicePixelRatioF() : qApp->devicePixelRatio(); 7760 const QPixmap pixmap = _helper->coloredIcon(icon, option->palette, iconSize, dpr, iconMode, iconState); 7761 drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); 7762 } 7763 7764 return true; 7765 } 7766 7767 //____________________________________________________________________________________________________ 7768 void Style::renderSpinBoxArrow(const SubControl &subControl, const QStyleOptionSpinBox *option, QPainter *painter, const QWidget *widget) const 7769 { 7770 const auto &palette(option->palette); 7771 const State &state(option->state); 7772 7773 // enable state 7774 bool enabled(state & State_Enabled); 7775 7776 // check steps enable step 7777 const bool atLimit((subControl == SC_SpinBoxUp && !(option->stepEnabled & QAbstractSpinBox::StepUpEnabled)) 7778 || (subControl == SC_SpinBoxDown && !(option->stepEnabled & QAbstractSpinBox::StepDownEnabled))); 7779 7780 // update enabled state accordingly 7781 enabled &= !atLimit; 7782 7783 // update mouse-over effect 7784 const bool mouseOver(enabled && (state & State_MouseOver)); 7785 7786 // check animation state 7787 const bool subControlHover(enabled && mouseOver && (option->activeSubControls & subControl)); 7788 _animations->spinBoxEngine().updateState(widget, subControl, subControlHover); 7789 7790 const bool animated(enabled && _animations->spinBoxEngine().isAnimated(widget, subControl)); 7791 const qreal opacity(_animations->spinBoxEngine().opacity(widget, subControl)); 7792 7793 auto color = _helper->arrowColor(palette, QPalette::Text); 7794 if (animated) { 7795 auto highlight = _helper->hoverColor(palette); 7796 color = KColorUtils::mix(color, highlight, opacity); 7797 7798 } else if (subControlHover) { 7799 color = _helper->hoverColor(palette); 7800 7801 } else if (atLimit) { 7802 color = _helper->arrowColor(palette, QPalette::Disabled, QPalette::Text); 7803 } 7804 7805 // arrow orientation 7806 ArrowOrientation orientation((subControl == SC_SpinBoxUp) ? ArrowUp : ArrowDown); 7807 7808 // arrow rect 7809 const auto arrowRect(subControlRect(CC_SpinBox, option, subControl, widget)); 7810 7811 // render 7812 _helper->renderArrow(painter, arrowRect, color, orientation); 7813 } 7814 7815 //______________________________________________________________________________ 7816 qreal Style::dialAngle(const QStyleOptionSlider *sliderOption, int value) const 7817 { 7818 // calculate angle at which handle needs to be drawn 7819 qreal angle(0); 7820 if (sliderOption->maximum == sliderOption->minimum) { 7821 angle = M_PI / 2; 7822 } else { 7823 qreal fraction(qreal(value - sliderOption->minimum) / qreal(sliderOption->maximum - sliderOption->minimum)); 7824 if (!sliderOption->upsideDown) { 7825 fraction = 1 - fraction; 7826 } 7827 7828 if (sliderOption->dialWrapping) { 7829 angle = 1.5 * M_PI - fraction * 2 * M_PI; 7830 } else { 7831 angle = (M_PI * 8 - fraction * 10 * M_PI) / 6; 7832 } 7833 } 7834 7835 return angle; 7836 } 7837 7838 //______________________________________________________________________________ 7839 const QWidget *Style::scrollBarParent(const QWidget *widget) const 7840 { 7841 // check widget and parent 7842 if (!(widget && widget->parentWidget())) { 7843 return nullptr; 7844 } 7845 7846 // try cast to scroll area. Must test both parent and grandparent 7847 QAbstractScrollArea *scrollArea; 7848 if (!(scrollArea = qobject_cast<QAbstractScrollArea *>(widget->parentWidget()))) { 7849 scrollArea = qobject_cast<QAbstractScrollArea *>(widget->parentWidget()->parentWidget()); 7850 } 7851 7852 // check scrollarea 7853 if (scrollArea && (widget == scrollArea->verticalScrollBar() || widget == scrollArea->horizontalScrollBar())) { 7854 return scrollArea; 7855 7856 } else if (widget->parentWidget()->inherits("KTextEditor::View")) { 7857 return widget->parentWidget(); 7858 7859 } else { 7860 return nullptr; 7861 } 7862 } 7863 7864 //______________________________________________________________________________ 7865 QColor Style::scrollBarArrowColor(const QStyleOptionSlider *option, const SubControl &control, const QWidget *widget) const 7866 { 7867 const auto &rect(option->rect); 7868 const auto &palette(option->palette); 7869 auto color(_helper->arrowColor(palette, QPalette::WindowText)); 7870 7871 if ((control == SC_ScrollBarSubLine && option->sliderValue == option->minimum) 7872 || (control == SC_ScrollBarAddLine && option->sliderValue == option->maximum)) { 7873 // manually disable arrow, to indicate that scrollbar is at limit 7874 return _helper->arrowColor(palette, QPalette::Disabled, QPalette::WindowText); 7875 } 7876 7877 const bool mouseOver(_animations->scrollBarEngine().isHovered(widget, control)); 7878 const bool animated(_animations->scrollBarEngine().isAnimated(widget, AnimationHover, control)); 7879 const qreal opacity(_animations->scrollBarEngine().opacity(widget, control)); 7880 7881 // retrieve mouse position from engine 7882 QPoint position(mouseOver ? _animations->scrollBarEngine().position(widget) : QPoint(-1, -1)); 7883 if (mouseOver && rect.contains(position)) { 7884 /* 7885 * need to update the arrow controlRect on fly because there is no 7886 * way to get it from the styles directly, outside of repaint events 7887 */ 7888 _animations->scrollBarEngine().setSubControlRect(widget, control, rect); 7889 } 7890 7891 if (rect.intersects(_animations->scrollBarEngine().subControlRect(widget, control))) { 7892 auto highlight = _helper->hoverColor(palette); 7893 if (animated) { 7894 color = KColorUtils::mix(color, highlight, opacity); 7895 7896 } else if (mouseOver) { 7897 color = highlight; 7898 } 7899 7900 } else if (option->state & State_MouseOver) { 7901 const bool activeIsSub = option->activeSubControls & SC_ScrollBarSubLine; 7902 const bool activeIsAdd = option->activeSubControls & SC_ScrollBarAddLine; 7903 const bool drawingSub = control == SC_ScrollBarSubLine; 7904 const bool drawingAdd = control == SC_ScrollBarAddLine; 7905 7906 if ((activeIsSub && drawingSub) || (activeIsAdd && drawingAdd)) { 7907 const auto highlight = _helper->hoverColor(palette); 7908 7909 color = highlight; 7910 } 7911 } 7912 7913 return color; 7914 } 7915 7916 //____________________________________________________________________________________ 7917 void Style::setTranslucentBackground(QWidget *widget) const 7918 { 7919 widget->setAttribute(Qt::WA_TranslucentBackground); 7920 7921 #ifdef Q_WS_WIN 7922 // FramelessWindowHint is needed on windows to make WA_TranslucentBackground work properly 7923 widget->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint); 7924 #endif 7925 } 7926 7927 //____________________________________________________________________________________ 7928 QIcon Style::toolBarExtensionIcon(StandardPixmap standardPixmap, const QStyleOption *option, const QWidget *widget) const 7929 { 7930 // store palette 7931 // due to Qt, it is not always safe to assume that either option, nor widget are defined 7932 QPalette palette; 7933 if (option) { 7934 palette = option->palette; 7935 } else if (widget) { 7936 palette = widget->palette(); 7937 } else { 7938 palette = QApplication::palette(); 7939 } 7940 7941 const auto direction = option ? option->direction : QGuiApplication::layoutDirection(); 7942 7943 // convenience class to map color to icon mode 7944 struct IconData { 7945 QColor _color; 7946 QIcon::Mode _mode; 7947 QIcon::State _state; 7948 }; 7949 7950 // map colors to icon states 7951 const QList<IconData> iconTypes = {{palette.color(QPalette::Active, QPalette::WindowText), QIcon::Normal, QIcon::Off}, 7952 {palette.color(QPalette::Active, QPalette::WindowText), QIcon::Selected, QIcon::Off}, 7953 {palette.color(QPalette::Active, QPalette::WindowText), QIcon::Active, QIcon::Off}, 7954 {palette.color(QPalette::Disabled, QPalette::WindowText), QIcon::Disabled, QIcon::Off}, 7955 7956 {palette.color(QPalette::Active, QPalette::HighlightedText), QIcon::Normal, QIcon::On}, 7957 {palette.color(QPalette::Active, QPalette::HighlightedText), QIcon::Selected, QIcon::On}, 7958 {palette.color(QPalette::Active, QPalette::WindowText), QIcon::Active, QIcon::On}, 7959 {palette.color(QPalette::Disabled, QPalette::WindowText), QIcon::Disabled, QIcon::On}}; 7960 7961 // default icon sizes 7962 static const QList<int> iconSizes = {8, 16, 22, 32, 48}; 7963 7964 // decide arrow orientation 7965 const ArrowOrientation orientation(standardPixmap == SP_ToolBarHorizontalExtensionButton ? (direction == Qt::RightToLeft ? ArrowLeft : ArrowRight) 7966 : ArrowDown); 7967 7968 // create icon and fill 7969 QIcon icon; 7970 for (const IconData &iconData : iconTypes) { 7971 for (const int &iconSize : iconSizes) { 7972 // create pixmap 7973 QPixmap pixmap(iconSize, iconSize); 7974 pixmap.fill(Qt::transparent); 7975 7976 // render 7977 QPainter painter(&pixmap); 7978 7979 // icon size 7980 const int fixedIconSize(pixelMetric(QStyle::PM_SmallIconSize, option, widget)); 7981 const QRect fixedRect(0, 0, fixedIconSize, fixedIconSize); 7982 7983 painter.setWindow(fixedRect); 7984 painter.translate(standardPixmap == SP_ToolBarHorizontalExtensionButton ? QPoint(1, 0) : QPoint(0, 1)); 7985 _helper->renderArrow(&painter, fixedRect, iconData._color, orientation); 7986 painter.end(); 7987 7988 // add to icon 7989 icon.addPixmap(pixmap, iconData._mode, iconData._state); 7990 } 7991 } 7992 7993 return icon; 7994 } 7995 7996 bool Style::isTabletMode() const 7997 { 7998 if (qEnvironmentVariableIsSet("BREEZE_IS_TABLET_MODE")) { 7999 return qEnvironmentVariableIntValue("BREEZE_IS_TABLET_MODE"); 8000 } 8001 #if BREEZE_HAVE_QTQUICK 8002 return TabletModeWatcher::self()->isTabletMode(); 8003 #else 8004 return false; 8005 #endif 8006 } 8007 8008 //____________________________________________________________________________________ 8009 QIcon Style::titleBarButtonIcon(StandardPixmap standardPixmap, const QStyleOption *option, const QWidget *widget) const 8010 { 8011 // map standardPixmap to button type 8012 ButtonType buttonType; 8013 switch (standardPixmap) { 8014 case SP_TitleBarNormalButton: 8015 buttonType = ButtonRestore; 8016 break; 8017 case SP_TitleBarMinButton: 8018 buttonType = ButtonMinimize; 8019 break; 8020 case SP_TitleBarMaxButton: 8021 buttonType = ButtonMaximize; 8022 break; 8023 case SP_TitleBarCloseButton: 8024 case SP_DockWidgetCloseButton: 8025 buttonType = ButtonClose; 8026 break; 8027 8028 default: 8029 return QIcon(); 8030 } 8031 8032 // store palette 8033 // due to Qt, it is not always safe to assume that either option, nor widget are defined 8034 QPalette palette; 8035 if (option) { 8036 palette = option->palette; 8037 } else if (widget) { 8038 palette = widget->palette(); 8039 } else { 8040 palette = QApplication::palette(); 8041 } 8042 8043 const bool isCloseButton(buttonType == ButtonClose && StyleConfigData::outlineCloseButton()); 8044 8045 palette.setCurrentColorGroup(QPalette::Active); 8046 const auto base(palette.color(QPalette::WindowText)); 8047 const auto selected(palette.color(QPalette::HighlightedText)); 8048 const auto negative(buttonType == ButtonClose ? _helper->negativeText(palette) : base); 8049 const auto negativeSelected(buttonType == ButtonClose ? _helper->negativeText(palette) : selected); 8050 8051 const bool invertNormalState(isCloseButton); 8052 8053 // convenience class to map color to icon mode 8054 struct IconData { 8055 QColor _color; 8056 bool _inverted; 8057 QIcon::Mode _mode; 8058 QIcon::State _state; 8059 }; 8060 8061 // map colors to icon states 8062 const QList<IconData> iconTypes = { 8063 8064 // state off icons 8065 {KColorUtils::mix(palette.color(QPalette::Window), base, 0.5), invertNormalState, QIcon::Normal, QIcon::Off}, 8066 {KColorUtils::mix(palette.color(QPalette::Window), base, 0.5), invertNormalState, QIcon::Selected, QIcon::Off}, 8067 {KColorUtils::mix(palette.color(QPalette::Window), negative, 0.5), true, QIcon::Active, QIcon::Off}, 8068 {KColorUtils::mix(palette.color(QPalette::Window), base, 0.2), invertNormalState, QIcon::Disabled, QIcon::Off}, 8069 8070 // state on icons 8071 {KColorUtils::mix(palette.color(QPalette::Window), negative, 0.7), true, QIcon::Normal, QIcon::On}, 8072 {KColorUtils::mix(palette.color(QPalette::Window), negativeSelected, 0.7), true, QIcon::Selected, QIcon::On}, 8073 {KColorUtils::mix(palette.color(QPalette::Window), negative, 0.7), true, QIcon::Active, QIcon::On}, 8074 {KColorUtils::mix(palette.color(QPalette::Window), base, 0.2), invertNormalState, QIcon::Disabled, QIcon::On} 8075 8076 }; 8077 8078 // default icon sizes 8079 static const QList<int> iconSizes = {8, 16, 22, 32, 48}; 8080 8081 // output icon 8082 QIcon icon; 8083 8084 for (const IconData &iconData : iconTypes) { 8085 for (const int &iconSize : iconSizes) { 8086 // create pixmap 8087 QPixmap pixmap(iconSize, iconSize); 8088 pixmap.fill(Qt::transparent); 8089 8090 // create painter and render 8091 QPainter painter(&pixmap); 8092 _helper->renderDecorationButton(&painter, pixmap.rect(), iconData._color, buttonType, iconData._inverted); 8093 8094 painter.end(); 8095 8096 // store 8097 icon.addPixmap(pixmap, iconData._mode, iconData._state); 8098 } 8099 } 8100 8101 return icon; 8102 } 8103 8104 //____________________________________________________________________ 8105 bool Style::isQtQuickControl(const QStyleOption *option, const QWidget *widget) const 8106 { 8107 #if BREEZE_HAVE_QTQUICK 8108 if (!widget && option) { 8109 if (const auto item = qobject_cast<QQuickItem *>(option->styleObject)) { 8110 _windowManager->registerQuickItem(item); 8111 return true; 8112 } 8113 } 8114 #else 8115 Q_UNUSED(widget); 8116 Q_UNUSED(option); 8117 #endif 8118 return false; 8119 } 8120 8121 //____________________________________________________________________ 8122 bool Style::showIconsInMenuItems() const 8123 { 8124 return !QApplication::testAttribute(Qt::AA_DontShowIconsInMenus); 8125 } 8126 8127 //____________________________________________________________________ 8128 bool Style::showIconsOnPushButtons() const 8129 { 8130 const KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("KDE")); 8131 return g.readEntry("ShowIconsOnPushButtons", true); 8132 } 8133 8134 //____________________________________________________________________ 8135 bool Style::hasAlteredBackground(const QWidget *widget) const 8136 { 8137 // check widget 8138 if (!widget) { 8139 return false; 8140 } 8141 8142 // check property 8143 const QVariant property(widget->property(PropertyNames::alteredBackground)); 8144 if (property.isValid()) { 8145 return property.toBool(); 8146 } 8147 8148 // check if widget is of relevant type 8149 bool hasAlteredBackground(false); 8150 if (const auto groupBox = qobject_cast<const QGroupBox *>(widget)) { 8151 hasAlteredBackground = !groupBox->isFlat(); 8152 } else if (const auto tabWidget = qobject_cast<const QTabWidget *>(widget)) { 8153 hasAlteredBackground = !tabWidget->documentMode(); 8154 } else if (qobject_cast<const QMenu *>(widget)) { 8155 hasAlteredBackground = true; 8156 } else if (StyleConfigData::dockWidgetDrawFrame() && qobject_cast<const QDockWidget *>(widget)) { 8157 hasAlteredBackground = true; 8158 } 8159 8160 if (widget->parentWidget() && !hasAlteredBackground) { 8161 hasAlteredBackground = this->hasAlteredBackground(widget->parentWidget()); 8162 } 8163 const_cast<QWidget *>(widget)->setProperty(PropertyNames::alteredBackground, hasAlteredBackground); 8164 return hasAlteredBackground; 8165 } 8166 8167 bool Style::hasHighlightNeutral(const QObject *widget, const QStyleOption *option, [[maybe_unused]] bool mouseOver, [[maybe_unused]] bool focus) const 8168 { 8169 if (!widget && (!option || !option->styleObject)) { 8170 return false; 8171 } 8172 8173 const QObject *styleObject = widget; 8174 if (!styleObject) { 8175 styleObject = option->styleObject; 8176 } 8177 8178 const QVariant property(styleObject->property(PropertyNames::highlightNeutral)); 8179 if (property.isValid()) { 8180 return property.toBool(); 8181 } 8182 return false; 8183 } 8184 8185 }