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, &copy);
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, &copy, painter, widget);
5278                 break;
5279             case Qt::RightArrow:
5280                 drawPrimitive(PE_IndicatorArrowRight, &copy, painter, widget);
5281                 break;
5282             case Qt::UpArrow:
5283                 drawPrimitive(PE_IndicatorArrowUp, &copy, painter, widget);
5284                 break;
5285             case Qt::DownArrow:
5286                 drawPrimitive(PE_IndicatorArrowDown, &copy, 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(&copy, reverseLayout ? SC_ScrollBarAddLine : SC_ScrollBarSubLine, widget);
6084             _helper->renderArrow(painter, leftSubButton, color, ArrowLeft);
6085 
6086             copy.rect = rightSubButton;
6087             color = scrollBarArrowColor(&copy, 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(&copy, SC_ScrollBarSubLine, widget);
6097             _helper->renderArrow(painter, topSubButton, color, ArrowUp);
6098 
6099             copy.rect = botSubButton;
6100             color = scrollBarArrowColor(&copy, SC_ScrollBarAddLine, widget);
6101             _helper->renderArrow(painter, botSubButton, color, ArrowDown);
6102         }
6103 
6104     } else if (_addLineButtons == SingleButton) {
6105         copy.rect = rect;
6106         color = scrollBarArrowColor(&copy, 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(&copy, reverseLayout ? SC_ScrollBarAddLine : SC_ScrollBarSubLine, widget);
6163             _helper->renderArrow(painter, leftSubButton, color, ArrowLeft);
6164 
6165             copy.rect = rightSubButton;
6166             color = scrollBarArrowColor(&copy, 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(&copy, SC_ScrollBarSubLine, widget);
6176             _helper->renderArrow(painter, topSubButton, color, ArrowUp);
6177 
6178             copy.rect = botSubButton;
6179             color = scrollBarArrowColor(&copy, SC_ScrollBarAddLine, widget);
6180             _helper->renderArrow(painter, botSubButton, color, ArrowDown);
6181         }
6182 
6183     } else if (_subLineButtons == SingleButton) {
6184         copy.rect = rect;
6185         color = scrollBarArrowColor(&copy, 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(&copy, painter, widget);
7165         } else {
7166             drawPrimitive(PE_PanelButtonTool, &copy, painter, widget);
7167         }
7168     }
7169 
7170     // arrow
7171     if (menuStyle == BreezePrivate::ToolButtonMenuArrowStyle::SubControl) {
7172         copy.rect = menuRect;
7173         drawPrimitive(PE_IndicatorButtonDropDown, &copy, painter, widget);
7174 
7175         copy.state &= ~State_MouseOver;
7176         copy.state &= ~State_Sunken;
7177         copy.state &= ~State_On;
7178         drawPrimitive(PE_IndicatorArrowDown, &copy, 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, &copy, 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, &copy, 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, &copy, 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 }