LCOV - code coverage report
Current view: top level - muse/framework/ui/view - widgetstyle.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 439 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2026 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program. If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "widgetstyle.h"
      24              : 
      25              : #include <QAbstractItemView>
      26              : #include <QApplication>
      27              : #include <QGroupBox>
      28              : #include <QLineEdit>
      29              : #include <QMenu>
      30              : #include <QPainter>
      31              : #include <QPalette>
      32              : #include <QStyle>
      33              : #include <QStyleFactory>
      34              : #include <QStyleOption>
      35              : #include <QTextEdit>
      36              : #include <QToolBar>
      37              : #include <QVariant>
      38              : 
      39              : #include "api/themeapi.h"
      40              : #include "iconcodes.h"
      41              : 
      42              : #include "log.h"
      43              : 
      44              : using namespace muse::ui;
      45              : using muse::api::ThemeApi;
      46              : 
      47              : static const QPen NO_BORDER(Qt::transparent, 0);
      48              : static const QBrush NO_FILL(Qt::transparent);
      49              : static const int DEFAULT_RADIUS = 3;
      50              : 
      51              : static const int GROUP_BOX_LABEL_SPACING = 2;
      52              : 
      53              : //! In QML, a border is drawn _inside_ a rectangle.
      54              : //! In C++, a border would normally be drawn half inside the rectangle, half outside.
      55              : //! In this function, we exactly replicate the behaviour from QML.
      56            0 : static void drawRoundedRect(QPainter* painter, const QRectF& rect, const qreal radius, const QBrush& brush = NO_FILL,
      57              :                             const QPen& pen = NO_BORDER)
      58              : {
      59            0 :     IF_ASSERT_FAILED(painter) {
      60            0 :         return;
      61              :     }
      62              : 
      63            0 :     const qreal bw = pen.width();
      64              : 
      65            0 :     if (bw <= 0 || pen.color().alpha() == 0) {
      66            0 :         if (brush == NO_FILL) {
      67            0 :             return;
      68              :         }
      69              : 
      70            0 :         painter->save();
      71            0 :         painter->setPen(NO_BORDER);
      72            0 :         painter->setBrush(brush);
      73            0 :         painter->drawRoundedRect(rect, radius, radius);
      74            0 :         painter->restore();
      75            0 :         return;
      76              :     }
      77              : 
      78            0 :     painter->save();
      79            0 :     painter->setPen(NO_BORDER);
      80            0 :     painter->setBrush(brush);
      81            0 :     painter->drawRoundedRect(rect.adjusted(bw, bw, -bw, -bw), radius - bw, radius - bw);
      82              : 
      83            0 :     const qreal corr = 0.5 * bw;
      84            0 :     painter->setPen(pen);
      85            0 :     painter->setBrush(NO_FILL);
      86            0 :     painter->drawRoundedRect(rect.adjusted(corr, corr, -corr, -corr), radius - corr, radius - corr);
      87            0 :     painter->restore();
      88              : }
      89              : 
      90            0 : WidgetStyle::WidgetStyle(std::shared_ptr<ThemeApi> t)
      91            0 :     : QProxyStyle(QStyleFactory::create("Fusion")), m_theme(t)
      92              : {
      93            0 : }
      94              : 
      95            0 : void WidgetStyle::init()
      96              : {
      97            0 :     connect(m_theme.get(), &ThemeApi::themeChanged, this, &WidgetStyle::update);
      98              : 
      99            0 :     update();
     100            0 : }
     101              : 
     102            0 : void WidgetStyle::update()
     103              : {
     104            0 :     QColor fontPrimaryColorDisabled = m_theme->fontPrimaryColor();
     105            0 :     fontPrimaryColorDisabled.setAlphaF(m_theme->itemOpacityDisabled());
     106              : 
     107            0 :     QColor linkColorDisabled = m_theme->linkColor();
     108            0 :     linkColorDisabled.setAlphaF(m_theme->itemOpacityDisabled());
     109              : 
     110            0 :     QColor backgroundPrimaryColorDisabled = m_theme->backgroundPrimaryColor();
     111            0 :     backgroundPrimaryColorDisabled.setAlphaF(m_theme->itemOpacityDisabled());
     112              : 
     113            0 :     QColor backgroundSecondaryColorDisabled = m_theme->backgroundSecondaryColor();
     114            0 :     backgroundSecondaryColorDisabled.setAlphaF(m_theme->itemOpacityDisabled());
     115              : 
     116            0 :     QColor buttonColorDisabled = m_theme->buttonColor();
     117            0 :     buttonColorDisabled.setAlphaF(m_theme->itemOpacityDisabled());
     118              : 
     119            0 :     QPalette palette(QApplication::palette());
     120            0 :     palette.setColor(QPalette::Window, m_theme->backgroundPrimaryColor());
     121            0 :     palette.setColor(QPalette::Disabled, QPalette::Window, backgroundPrimaryColorDisabled);
     122            0 :     palette.setColor(QPalette::WindowText, m_theme->fontPrimaryColor());
     123            0 :     palette.setColor(QPalette::Disabled, QPalette::WindowText, fontPrimaryColorDisabled);
     124              : 
     125            0 :     palette.setColor(QPalette::Base, m_theme->backgroundSecondaryColor());
     126            0 :     palette.setColor(QPalette::Disabled, QPalette::Base, backgroundSecondaryColorDisabled);
     127            0 :     palette.setColor(QPalette::AlternateBase, m_theme->backgroundSecondaryColor());
     128            0 :     palette.setColor(QPalette::Disabled, QPalette::AlternateBase, backgroundSecondaryColorDisabled);
     129              : 
     130            0 :     palette.setColor(QPalette::Text, m_theme->fontPrimaryColor());
     131            0 :     palette.setColor(QPalette::Disabled, QPalette::Text, fontPrimaryColorDisabled);
     132              : 
     133            0 :     palette.setColor(QPalette::Link, m_theme->linkColor());
     134            0 :     palette.setColor(QPalette::Disabled, QPalette::Link, linkColorDisabled);
     135              : 
     136            0 :     palette.setColor(QPalette::Button, m_theme->buttonColor());
     137            0 :     palette.setColor(QPalette::Disabled, QPalette::Button, buttonColorDisabled);
     138            0 :     palette.setColor(QPalette::ButtonText, m_theme->fontPrimaryColor());
     139            0 :     palette.setColor(QPalette::Disabled, QPalette::ButtonText, fontPrimaryColorDisabled);
     140              : 
     141            0 :     palette.setColor(QPalette::ToolTipBase, m_theme->popupBackgroundColor());
     142            0 :     palette.setColor(QPalette::ToolTipText, m_theme->fontPrimaryColor());
     143              : 
     144            0 :     palette.setColor(QPalette::Highlight, m_theme->accentColor());
     145            0 :     palette.setColor(QPalette::HighlightedText, m_theme->fontPrimaryColor());
     146              : 
     147            0 :     palette.setColor(QPalette::PlaceholderText, m_theme->fontPrimaryColor());
     148            0 :     palette.setColor(QPalette::Disabled, QPalette::PlaceholderText, fontPrimaryColorDisabled);
     149              : 
     150            0 :     QApplication::setStyle(this);
     151            0 :     QApplication::setPalette(palette);
     152              : 
     153            0 :     QFont font = m_theme->bodyFont();
     154            0 :     QString styleSheet = QString("* { font: %1px \"%2\" } ")
     155            0 :                          .arg(QString::number(font.pixelSize()), font.family());
     156            0 :     qApp->setStyleSheet(styleSheet);
     157            0 : }
     158              : 
     159            0 : void WidgetStyle::polish(QWidget* widget)
     160              : {
     161            0 :     QProxyStyle::polish(widget);
     162              : 
     163            0 :     if (qobject_cast<QAbstractItemView*>(widget)
     164            0 :         || qobject_cast<QGroupBox*>(widget)
     165            0 :         || qobject_cast<QLineEdit*>(widget)) {
     166              :         // Make hovering work
     167            0 :         widget->setMouseTracking(true);
     168            0 :         widget->setAttribute(Qt::WA_Hover, true);
     169              :     }
     170            0 : }
     171              : 
     172            0 : void WidgetStyle::unpolish(QWidget* widget)
     173              : {
     174            0 :     QProxyStyle::unpolish(widget);
     175              : 
     176            0 :     if (qobject_cast<QAbstractItemView*>(widget)
     177            0 :         || qobject_cast<QGroupBox*>(widget)
     178            0 :         || qobject_cast<QLineEdit*>(widget)) {
     179            0 :         widget->setMouseTracking(false);
     180            0 :         widget->setAttribute(Qt::WA_Hover, false);
     181              :     }
     182            0 : }
     183              : 
     184            0 : void WidgetStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption* option, QPainter* painter,
     185              :                                 const QWidget* widget) const
     186              : {
     187            0 :     StyleState styleState;
     188            0 :     styleState.enabled = option->state & State_Enabled;
     189            0 :     styleState.hovered = option->state & State_MouseOver;
     190            0 :     styleState.pressed = option->state & State_Sunken;
     191            0 :     styleState.focused = (option->state & State_KeyboardFocusChange) && (option->state & State_HasFocus);
     192              : 
     193            0 :     painter->save();
     194            0 :     painter->setRenderHint(QPainter::Antialiasing, true);
     195              : 
     196              :     //! NOTE This drawing code is based on the implementation in QML.
     197            0 :     switch (element) {
     198              :     // Buttons (and ComboBoxes)
     199            0 :     case QStyle::PE_PanelButtonCommand: {
     200            0 :         auto buttonOption = qstyleoption_cast<const QStyleOptionButton*>(option);
     201            0 :         const bool accentButton = (buttonOption && buttonOption->features & QStyleOptionButton::DefaultButton)
     202            0 :                                   || option->state & State_On;
     203            0 :         const bool flat = (buttonOption && buttonOption->features & QStyleOptionButton::Flat)
     204            0 :                           && !(option->state & State_On);
     205              : 
     206            0 :         QColor paletteColor = widget ? widget->palette().color(QPalette::Button) : QColor();
     207            0 :         const QColor background = paletteColor.isValid() ? paletteColor : m_theme->buttonColor();
     208              : 
     209            0 :         drawButtonBackground(painter, option->rect, styleState, accentButton, flat, background);
     210            0 :     } break;
     211            0 :     case QStyle::PE_FrameDefaultButton: {
     212            0 :         auto buttonOption = qstyleoption_cast<const QStyleOptionButton*>(option);
     213            0 :         const bool flat = (buttonOption && buttonOption->features & QStyleOptionButton::Flat)
     214            0 :                           && !(option->state & State_On);
     215            0 :         if (flat && styleState.focused) {
     216              :             // For other buttons, this is done in `drawButtonBackground`, but that is not called for flat buttons
     217            0 :             drawRoundedRect(painter, option->rect, DEFAULT_RADIUS, NO_FILL,
     218            0 :                             QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     219              :         }
     220            0 :     } break;
     221              : 
     222              :     // Checkboxes
     223            0 :     case QStyle::PE_IndicatorCheckBox: {
     224            0 :         const bool indeterminate = option->state & State_NoChange;
     225            0 :         const bool checked = option->state & State_On;
     226            0 :         const bool inMenu = qobject_cast<const QMenu*>(widget);
     227              : 
     228            0 :         drawCheckboxIndicator(painter, option->rect, styleState, checked, indeterminate, inMenu);
     229            0 :     } break;
     230              : 
     231              :     // Radio buttons
     232            0 :     case QStyle::PE_IndicatorRadioButton: {
     233            0 :         const bool selected = option->state & State_On;
     234              : 
     235            0 :         drawRadioButtonIndicator(painter, option->rect, styleState, selected);
     236            0 :     } break;
     237              : 
     238            0 :     case QStyle::PE_FrameLineEdit: {
     239            0 :         const bool editing = option->state & State_HasFocus;
     240              : 
     241            0 :         drawLineEditBackground(painter, option->rect, styleState, editing);
     242            0 :     } break;
     243              : 
     244            0 :     case QStyle::PE_FrameFocusRect: {
     245            0 :         bool isTreeWidget = option->styleObject && option->styleObject->inherits("QTreeWidget");
     246            0 :         if (isTreeWidget) {
     247            0 :             drawRoundedRect(painter, option->rect, 1, NO_FILL, QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     248              :         }
     249              : 
     250              :         //! NOTE: need for removing frame focus rectangle
     251            0 :     } break;
     252              : 
     253              :     // Indicator icons
     254            0 :     case QStyle::PE_IndicatorSpinUp:
     255              :     case QStyle::PE_IndicatorSpinDown:
     256              :     case QStyle::PE_IndicatorSpinPlus:
     257              :     case QStyle::PE_IndicatorSpinMinus: {
     258            0 :         drawIndicatorIcon(painter, option->rect, styleState, element);
     259            0 :     } break;
     260              : 
     261              :     // ViewItem
     262            0 :     case QStyle::PE_PanelItemViewItem: {
     263            0 :         bool selected = option->state & State_Selected;
     264              : 
     265            0 :         drawViewItemBackground(painter, option->rect, styleState, selected);
     266            0 :     } break;
     267              : 
     268              :     // Toolbar
     269            0 :     case QStyle::PE_PanelToolBar: {
     270            0 :         painter->fillRect(option->rect, m_theme->backgroundPrimaryColor());
     271            0 :     } break;
     272            0 :     case QStyle::PE_IndicatorToolBarHandle: {
     273            0 :         drawToolbarGrip(painter, option->rect, option->state & State_Horizontal);
     274            0 :     } break;
     275              : 
     276              :     // GroupBox
     277            0 :     case QStyle::PE_FrameGroupBox: {
     278            0 :         drawRoundedRect(painter, option->rect, DEFAULT_RADIUS, QBrush(QColor::fromRgba(0x03000000)),
     279            0 :                         QPen(m_theme->strokeColor(), fmax(m_theme->borderWidth(), 1.0)));
     280            0 :     } break;
     281              : 
     282              :     // Menu
     283            0 :     case QStyle::PE_PanelMenu: {
     284            0 :         drawRoundedRect(painter, option->rect, 1, m_theme->backgroundPrimaryColor(), QPen(m_theme->strokeColor(), m_theme->borderWidth()));
     285            0 :     } break;
     286            0 :     case QStyle::PE_FrameMenu: {
     287            0 :         drawRoundedRect(painter, option->rect, 1, NO_FILL, QPen(m_theme->strokeColor(), m_theme->borderWidth()));
     288            0 :     } break;
     289              : 
     290            0 :     case QStyle::PE_Frame: {
     291            0 :         if (qobject_cast<const QTextEdit*>(widget) != nullptr) {
     292            0 :             if (styleState.enabled) {
     293            0 :                 drawRoundedRect(painter, option->rect, DEFAULT_RADIUS, NO_FILL, QPen(m_theme->strokeColor(), m_theme->borderWidth()));
     294              :             } else {
     295            0 :                 QColor penBorderColor = m_theme->strokeColor();
     296            0 :                 penBorderColor.setAlphaF(m_theme->itemOpacityDisabled());
     297            0 :                 drawRoundedRect(painter, option->rect, DEFAULT_RADIUS, NO_FILL, QPen(penBorderColor, m_theme->borderWidth()));
     298              :             }
     299              :         }
     300            0 :     } break;
     301              : 
     302            0 :     default:
     303            0 :         QProxyStyle::drawPrimitive(element, option, painter, widget);
     304            0 :         break;
     305              :     }
     306              : 
     307            0 :     painter->restore();
     308            0 : }
     309              : 
     310            0 : void WidgetStyle::drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex* option, QPainter* painter,
     311              :                                      const QWidget* widget) const
     312              : {
     313            0 :     StyleState styleState;
     314            0 :     styleState.enabled = option->state & State_Enabled;
     315            0 :     styleState.hovered = option->state & State_MouseOver;
     316            0 :     styleState.pressed = option->state & State_Sunken;
     317            0 :     styleState.focused = (option->state & State_KeyboardFocusChange) && (option->state & State_HasFocus);
     318              : 
     319            0 :     switch (control) {
     320            0 :     case CC_ScrollBar: {
     321            0 :         QProxyStyle::drawComplexControl(control, option, painter, widget);
     322              : 
     323            0 :         if (m_theme->configuration()->isHighContrast()) {
     324            0 :             QRect scrollBarHandle = QProxyStyle::subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
     325              : 
     326            0 :             QColor handleColor = m_theme->fontPrimaryColor();
     327            0 :             handleColor.setAlphaF(
     328            0 :                 !styleState.enabled ? m_theme->buttonOpacityNormal()
     329            0 :                 * m_theme->itemOpacityDisabled() : styleState.pressed ? m_theme->buttonOpacityHit() : styleState.hovered ? m_theme->buttonOpacityHover() : m_theme->buttonOpacityNormal());
     330              : 
     331            0 :             drawRoundedRect(painter, option->rect, 1, NO_FILL, QPen(m_theme->strokeColor(), m_theme->borderWidth()));
     332            0 :             drawRoundedRect(painter, scrollBarHandle, 1, handleColor, NO_BORDER);
     333              :         }
     334            0 :     } break;
     335              : 
     336            0 :     case CC_SpinBox: {
     337            0 :         QProxyStyle::drawComplexControl(control, option, painter, widget);
     338              : 
     339            0 :         QRect spinBoxFrame = QProxyStyle::subControlRect(CC_SpinBox, option, SC_SpinBoxFrame, widget);
     340            0 :         if (styleState.focused) {
     341            0 :             drawRoundedRect(painter, spinBoxFrame, DEFAULT_RADIUS, NO_FILL,
     342            0 :                             QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     343              :         }
     344            0 :     } break;
     345              : 
     346            0 :     default: QProxyStyle::drawComplexControl(control, option, painter, widget);
     347              :     }
     348            0 : }
     349              : 
     350            0 : QRect WidgetStyle::subControlRect(QStyle::ComplexControl control, const QStyleOptionComplex* option, QStyle::SubControl subControl,
     351              :                                   const QWidget* widget) const
     352              : {
     353              :     //QRect commonStyleRect = QCommonStyle::subControlRect(control, option, subControl, widget);
     354            0 :     QRect proxyStyleRect = QProxyStyle::subControlRect(control, option, subControl, widget);
     355              : 
     356            0 :     switch (control) {
     357            0 :     case QStyle::CC_GroupBox:
     358            0 :         if (const QStyleOptionGroupBox* optionGroupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option)) {
     359            0 :             int indicatorWidth = 0;
     360            0 :             int indicatorHeight = 0;
     361            0 :             int indicatorSpacing = 0;
     362            0 :             const QSize textSize = option->fontMetrics.boundingRect(optionGroupBox->text).size() + QSize(2, 2);
     363              : 
     364            0 :             const bool checkable = option->subControls & QStyle::SC_GroupBoxCheckBox;
     365              : 
     366            0 :             if (checkable) {
     367              :                 const bool isRadioButtonGroupBox
     368            0 :                     =widget && strcmp(widget->metaObject()->className(), "muse::uicomponents::RadioButtonGroupBox") == 0;
     369              : 
     370            0 :                 indicatorWidth = pixelMetric(isRadioButtonGroupBox ? PM_ExclusiveIndicatorWidth : PM_IndicatorWidth, option, widget);
     371            0 :                 indicatorHeight = pixelMetric(isRadioButtonGroupBox ? PM_ExclusiveIndicatorHeight : PM_IndicatorHeight, option, widget);
     372              :                 indicatorSpacing
     373            0 :                     = pixelMetric(isRadioButtonGroupBox ? PM_RadioButtonLabelSpacing : PM_CheckBoxLabelSpacing, option, widget);
     374              :             }
     375              : 
     376            0 :             if (subControl == SC_GroupBoxFrame) {
     377            0 :                 int topMargin = std::max(indicatorHeight, textSize.height()) + GROUP_BOX_LABEL_SPACING;
     378            0 :                 return option->rect.adjusted(0, topMargin, 0, 0);
     379              :             }
     380              : 
     381            0 :             if (subControl == SC_GroupBoxContents) {
     382            0 :                 int margin = 3;
     383            0 :                 int topMargin = margin + std::max(indicatorHeight, textSize.height()) + GROUP_BOX_LABEL_SPACING;
     384            0 :                 return option->rect.adjusted(margin, topMargin, -margin, -margin);
     385              :             }
     386              : 
     387            0 :             const int width = textSize.width() + (checkable ? indicatorWidth + indicatorSpacing : 0);
     388            0 :             QRect rect;
     389              : 
     390            0 :             if (option->rect.width() > width) {
     391            0 :                 switch (optionGroupBox->textAlignment & Qt::AlignHorizontal_Mask) {
     392            0 :                 case Qt::AlignHCenter:
     393            0 :                     rect.moveLeft((option->rect.width() - width) / 2);
     394            0 :                     break;
     395            0 :                 case Qt::AlignRight:
     396            0 :                     rect.moveLeft(option->rect.width() - width);
     397            0 :                     break;
     398              :                 }
     399              :             }
     400              : 
     401            0 :             if (subControl == SC_GroupBoxCheckBox) {
     402            0 :                 rect.setWidth(indicatorWidth);
     403            0 :                 rect.setHeight(indicatorHeight);
     404            0 :                 rect.moveTop(textSize.height() > indicatorHeight ? (textSize.height() - indicatorHeight) / 2 : 0);
     405            0 :             } else if (subControl == SC_GroupBoxLabel) {
     406            0 :                 rect.setSize(textSize);
     407            0 :                 if (checkable) {
     408            0 :                     rect.moveTop(textSize.height() < indicatorHeight ? (indicatorHeight - textSize.height()) / 2 : 0);
     409            0 :                     rect.translate(indicatorWidth + indicatorSpacing, 0);
     410              :                 }
     411              :             }
     412              : 
     413            0 :             return visualRect(option->direction, option->rect, rect);
     414              :         }
     415            0 :         break;
     416            0 :     default:
     417            0 :         break;
     418              :     }
     419              : 
     420            0 :     return proxyStyleRect;
     421              : }
     422              : 
     423            0 : int WidgetStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption* option, const QWidget* widget) const
     424              : {
     425              :     //! NOTE These metrics are based on the implementation in QML.
     426            0 :     switch (metric) {
     427            0 :     case PM_IndicatorWidth: // Checkbox
     428              :     case PM_IndicatorHeight:
     429              :     case PM_ExclusiveIndicatorWidth: // Radio button
     430              :     case PM_ExclusiveIndicatorHeight:
     431            0 :         return 20;
     432            0 :     case PM_CheckBoxLabelSpacing: // Checkbox
     433              :     case PM_RadioButtonLabelSpacing: // Radio button
     434            0 :         return 6;
     435            0 :     case PM_ToolBarHandleExtent: // Toolbars
     436            0 :         return 32;
     437            0 :     default:
     438            0 :         break;
     439              :     }
     440              : 
     441            0 :     return QProxyStyle::pixelMetric(metric, option, widget);
     442              : }
     443              : 
     444            0 : QSize WidgetStyle::sizeFromContents(QStyle::ContentsType type, const QStyleOption* option, const QSize& contentsSize,
     445              :                                     const QWidget* widget) const
     446              : {
     447            0 :     QSize commonStyleSize = QCommonStyle::sizeFromContents(type, option, contentsSize, widget);
     448            0 :     QSize proxyStyleSize = QProxyStyle::sizeFromContents(type, option, contentsSize, widget);
     449              : 
     450              :     //! NOTE These calculations are based on the implementation in QML.
     451            0 :     switch (type) {
     452            0 :     case CT_PushButton:
     453            0 :         return QSize(std::max(contentsSize.width() + 32, 132),
     454            0 :                      contentsSize.height() + 14);
     455            0 :     case CT_ToolButton:
     456            0 :         return contentsSize.expandedTo(QSize(30, 30));
     457            0 :     case CT_ComboBox:
     458              :     case CT_LineEdit:
     459            0 :         return proxyStyleSize.expandedTo(QSize(30, 30));
     460            0 :     case CT_SpinBox:
     461            0 :         return QSize(proxyStyleSize.width(), 26); // results in a height of 30
     462            0 :     case CT_GroupBox: {
     463            0 :         const QGroupBox* groupBox = qobject_cast<const QGroupBox*>(widget);
     464            0 :         const bool checkable = groupBox && groupBox->isCheckable();
     465              : 
     466            0 :         if (checkable) {
     467              :             const bool isRadioButtonGroupBox
     468            0 :                 =widget && strcmp(widget->metaObject()->className(), "muse::uicomponents::RadioButtonGroupBox") == 0;
     469              : 
     470            0 :             int pm = pixelMetric(isRadioButtonGroupBox ? PM_ExclusiveIndicatorHeight : PM_IndicatorHeight, option, widget);
     471            0 :             return commonStyleSize + QSize(0, std::max(pm, option->fontMetrics.height()));
     472              :         }
     473              : 
     474            0 :         return commonStyleSize + QSize(0, option->fontMetrics.height());
     475              :     } break;
     476            0 :     case CT_ItemViewItem:
     477            0 :         return commonStyleSize.expandedTo(QSize(20, 20));
     478            0 :     case CT_MenuItem:
     479            0 :         if (const QStyleOptionMenuItem* optionMenuItem = qstyleoption_cast<const QStyleOptionMenuItem*>(option)) {
     480            0 :             if (optionMenuItem->menuItemType == QStyleOptionMenuItem::Separator) {
     481            0 :                 return proxyStyleSize;
     482              :             }
     483              :         }
     484            0 :         return proxyStyleSize.expandedTo(QSize(30, 30));
     485            0 :     default:
     486            0 :         break;
     487              :     }
     488              : 
     489            0 :     return proxyStyleSize;
     490              : }
     491              : 
     492            0 : QIcon WidgetStyle::standardIcon(QStyle::StandardPixmap standardIcon, const QStyleOption* option, const QWidget* widget) const
     493              : {
     494            0 :     switch (standardIcon) {
     495            0 :     case SP_DialogOkButton:
     496              :     case SP_DialogCancelButton:
     497              :     case SP_DialogHelpButton:
     498              :     case SP_DialogOpenButton:
     499              :     case SP_DialogSaveButton:
     500              :     case SP_DialogCloseButton:
     501              :     case SP_DialogApplyButton:
     502              :     case SP_DialogResetButton:
     503              :     case SP_DialogDiscardButton:
     504              :     case SP_DialogYesButton:
     505              :     case SP_DialogNoButton:
     506              :     case SP_DialogYesToAllButton:
     507              :     case SP_DialogNoToAllButton:
     508              :     case SP_DialogSaveAllButton:
     509              :     case SP_DialogAbortButton:
     510              :     case SP_DialogRetryButton:
     511              :     case SP_DialogIgnoreButton:
     512            0 :         return {};
     513            0 :     default:
     514            0 :         return QProxyStyle::standardIcon(standardIcon, option, widget);
     515              :     }
     516              : }
     517              : 
     518            0 : int WidgetStyle::styleHint(QStyle::StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* returnData) const
     519              : {
     520            0 :     switch (hint) {
     521            0 :     case SH_DitherDisabledText:
     522              :     case SH_EtchDisabledText:
     523            0 :         return false;
     524            0 :     case SH_ItemView_ScrollMode:
     525            0 :         return QAbstractItemView::ScrollPerPixel;
     526            0 :     default:
     527            0 :         break;
     528              :     }
     529              : 
     530            0 :     return QProxyStyle::styleHint(hint, option, widget, returnData);
     531              : }
     532              : 
     533              : // ====================================================
     534              : // QStyle elements drawing
     535              : // ====================================================
     536              : 
     537            0 : void WidgetStyle::drawButtonBackground(QPainter* painter, const QRect& rect, const StyleState& styleState, bool accentButton, bool flat,
     538              :                                        const QColor& defaultBackground) const
     539              : {
     540            0 :     QColor backgroundColor(accentButton ? m_theme->accentColor() : defaultBackground);
     541              : 
     542            0 :     if (styleState.enabled) {
     543            0 :         backgroundColor.setAlphaF(styleState.pressed ? m_theme->buttonOpacityHit()
     544            0 :                                   : styleState.hovered ? m_theme->buttonOpacityHover()
     545            0 :                                   : !flat ? m_theme->buttonOpacityNormal()
     546              :                                   : 0.0);
     547              : 
     548            0 :         if (m_theme->configuration()->isHighContrast()) {
     549            0 :             QColor penBorderColor(m_theme->strokeColor());
     550            0 :             penBorderColor.setAlphaF(styleState.pressed ? m_theme->buttonOpacityHit()
     551            0 :                                      : styleState.hovered ? m_theme->buttonOpacityHover()
     552            0 :                                      : m_theme->buttonOpacityNormal());
     553              : 
     554            0 :             drawRoundedRect(painter, rect, DEFAULT_RADIUS, backgroundColor, QPen(penBorderColor, m_theme->borderWidth()));
     555              :         } else {
     556            0 :             drawRoundedRect(painter, rect, DEFAULT_RADIUS, backgroundColor, NO_BORDER);
     557              :         }
     558              :     } else {
     559            0 :         backgroundColor.setAlphaF(flat ? 0.0 : m_theme->buttonOpacityNormal() * m_theme->itemOpacityDisabled());
     560              : 
     561            0 :         drawRoundedRect(painter, rect, DEFAULT_RADIUS, backgroundColor, NO_BORDER);
     562              :     }
     563              : 
     564            0 :     if (styleState.focused) {
     565            0 :         drawRoundedRect(painter, rect, DEFAULT_RADIUS, NO_FILL, QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     566              :     }
     567            0 : }
     568              : 
     569            0 : void WidgetStyle::drawCheckboxIndicator(QPainter* painter, const QRect& rect, const StyleState& styleState, bool checked,
     570              :                                         bool indeterminate,
     571              :                                         bool inMenu) const
     572              : {
     573            0 :     QColor backgroundColor = m_theme->buttonColor();
     574            0 :     const qreal borderRadius = 2;
     575              : 
     576            0 :     if (!inMenu) {
     577            0 :         backgroundColor.setAlphaF(!styleState.enabled ? m_theme->buttonOpacityNormal() * m_theme->itemOpacityDisabled()
     578            0 :                                   : styleState.pressed ? m_theme->buttonOpacityHit()
     579            0 :                                   : styleState.hovered ? m_theme->buttonOpacityHover()
     580            0 :                                   : m_theme->buttonOpacityNormal());
     581              : 
     582            0 :         QColor penBorderColor(Qt::transparent);
     583            0 :         int penBorderWidth = 0;
     584            0 :         if (m_theme->configuration()->isHighContrast()) {
     585            0 :             penBorderColor = m_theme->strokeColor();
     586            0 :             penBorderWidth = styleState.enabled ? m_theme->borderWidth() : 0;
     587              :         } else {
     588            0 :             penBorderWidth = styleState.enabled && (styleState.hovered || styleState.pressed) ? m_theme->borderWidth() : 0;
     589              :         }
     590              : 
     591            0 :         penBorderColor.setAlphaF(
     592            0 :             styleState.pressed ? m_theme->buttonOpacityHit() : styleState.hovered ? m_theme->buttonOpacityHover() : m_theme->buttonOpacityNormal());
     593            0 :         drawRoundedRect(painter, rect, borderRadius, backgroundColor, QPen(penBorderColor, penBorderWidth));
     594              :     }
     595              : 
     596            0 :     if (styleState.focused) {
     597            0 :         drawRoundedRect(painter, rect, borderRadius, NO_FILL, QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     598              :     }
     599              : 
     600            0 :     if (checked || indeterminate) {
     601            0 :         QColor tickColor = m_theme->fontPrimaryColor();
     602            0 :         if (!styleState.enabled) {
     603            0 :             tickColor.setAlphaF(m_theme->itemOpacityDisabled());
     604              :         }
     605            0 :         painter->setPen(tickColor);
     606            0 :         painter->setFont(m_theme->iconsFont());
     607            0 :         painter->drawText(rect, Qt::AlignCenter,
     608              :                           iconCodeToChar(indeterminate ? IconCode::Code::MINUS : IconCode::Code::TICK_RIGHT_ANGLE));
     609              :     }
     610            0 : }
     611              : 
     612            0 : void WidgetStyle::drawRadioButtonIndicator(QPainter* painter, const QRect& rect, const StyleState& styleState, bool selected) const
     613              : {
     614            0 :     QColor borderColor = m_theme->fontPrimaryColor();
     615            0 :     QColor backgroundColor = m_theme->textFieldColor();
     616              : 
     617            0 :     if (!styleState.enabled) {
     618            0 :         borderColor.setAlphaF(m_theme->itemOpacityDisabled());
     619            0 :     } else if (styleState.pressed) {
     620            0 :         borderColor.setAlphaF(m_theme->buttonOpacityHit());
     621            0 :     } else if (styleState.hovered) {
     622            0 :         borderColor.setAlphaF(m_theme->buttonOpacityHover());
     623              :     } else {
     624            0 :         borderColor.setAlphaF(m_theme->buttonOpacityNormal());
     625              :     }
     626              : 
     627            0 :     const int borderWidth = 1;
     628            0 :     qreal outerCircleRadius = 10; // diameter = 20
     629              : 
     630            0 :     if (styleState.focused) {
     631            0 :         const qreal focusCircleRadius = outerCircleRadius;
     632            0 :         const QRect focusCircleRect(rect.center() + QPoint(1, 1) - QPoint(focusCircleRadius, focusCircleRadius),
     633            0 :                                     QSize(focusCircleRadius, focusCircleRadius) * 2);
     634            0 :         drawRoundedRect(painter, focusCircleRect, focusCircleRadius, NO_FILL,
     635            0 :                         QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     636              : 
     637            0 :         outerCircleRadius -= m_theme->navCtrlBorderWidth();
     638              :     }
     639              : 
     640            0 :     const QRect outerCircleRect(rect.center() + QPoint(1, 1) - QPoint(outerCircleRadius, outerCircleRadius),
     641            0 :                                 QSize(outerCircleRadius, outerCircleRadius) * 2);
     642            0 :     drawRoundedRect(painter, outerCircleRect, outerCircleRadius, backgroundColor, QPen(borderColor, borderWidth));
     643              : 
     644            0 :     if (selected || styleState.pressed) {
     645            0 :         QColor centerColor = m_theme->accentColor();
     646            0 :         const int innerCircleRadius = 5; // diameter = 10
     647            0 :         const QRect innerCircleRect(rect.center() + QPoint(1, 1) - QPoint(innerCircleRadius, innerCircleRadius),
     648            0 :                                     QSize(innerCircleRadius, innerCircleRadius) * 2);
     649            0 :         drawRoundedRect(painter, innerCircleRect, innerCircleRadius, centerColor);
     650              :     }
     651            0 : }
     652              : 
     653            0 : void WidgetStyle::drawLineEditBackground(QPainter* painter, const QRect& rect, const StyleState& styleState, bool editing) const
     654              : {
     655            0 :     QColor backgroundColor = m_theme->textFieldColor();
     656            0 :     backgroundColor.setAlphaF(!styleState.enabled ? m_theme->itemOpacityDisabled() : (editing ? 1 : (styleState.hovered ? 0.6 : 1)));
     657              : 
     658            0 :     if (styleState.focused) {
     659            0 :         drawRoundedRect(painter, rect, DEFAULT_RADIUS, NO_FILL, QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     660              :     }
     661              : 
     662            0 :     QRect editRect = rect.adjusted(m_theme->navCtrlBorderWidth(), m_theme->navCtrlBorderWidth(),
     663            0 :                                    -m_theme->navCtrlBorderWidth(), -m_theme->navCtrlBorderWidth());
     664            0 :     QColor penBorderColor = editing ? m_theme->accentColor() : m_theme->strokeColor();
     665            0 :     penBorderColor.setAlphaF(editing ? 1 : (styleState.hovered ? 0.6 : 1));
     666            0 :     int borderWidth = m_theme->configuration()->isHighContrast() ? m_theme->borderWidth() : 1;
     667              : 
     668            0 :     if (styleState.enabled) {
     669            0 :         drawRoundedRect(painter, editRect, DEFAULT_RADIUS, backgroundColor, QPen(penBorderColor, borderWidth));
     670              :     } else {
     671            0 :         drawRoundedRect(painter, editRect, DEFAULT_RADIUS, backgroundColor, QPen(penBorderColor, 1));
     672              :     }
     673            0 : }
     674              : 
     675            0 : void WidgetStyle::drawIndicatorIcon(QPainter* painter, const QRect& rect, const StyleState& styleState,
     676              :                                     QStyle::PrimitiveElement element) const
     677              : {
     678            0 :     QColor color = m_theme->fontPrimaryColor();
     679            0 :     if (!styleState.enabled) {
     680            0 :         color.setAlphaF(m_theme->itemOpacityDisabled());
     681              :     }
     682            0 :     painter->setPen(color);
     683            0 :     painter->setFont(m_theme->iconsFont());
     684              : 
     685              :     IconCode::Code code;
     686            0 :     switch (element) {
     687            0 :     case QStyle::PE_IndicatorSpinPlus:
     688            0 :         code = IconCode::Code::PLUS;
     689            0 :         break;
     690            0 :     case QStyle::PE_IndicatorSpinMinus:
     691            0 :         code = IconCode::Code::MINUS;
     692            0 :         break;
     693            0 :     case QStyle::PE_IndicatorSpinUp:
     694            0 :         code = IconCode::Code::SMALL_ARROW_UP;
     695            0 :         break;
     696            0 :     case QStyle::PE_IndicatorSpinDown:
     697            0 :         code = IconCode::Code::SMALL_ARROW_DOWN;
     698            0 :         break;
     699            0 :     default:
     700            0 :         return;
     701              :     }
     702              : 
     703            0 :     painter->drawText(rect, Qt::AlignCenter, iconCodeToChar(code));
     704            0 :     drawRoundedRect(painter, rect, 1, NO_FILL, QPen(m_theme->strokeColor(), m_theme->borderWidth())); //does nothing apparently
     705              : }
     706              : 
     707            0 : void WidgetStyle::drawViewItemBackground(QPainter* painter, const QRect& rect, const StyleState& styleState, bool selected) const
     708              : {
     709            0 :     QColor backgroundColor(Qt::transparent);
     710            0 :     if (selected) {
     711            0 :         backgroundColor = m_theme->accentColor();
     712            0 :         backgroundColor.setAlphaF(
     713            0 :             styleState.enabled ? m_theme->accentOpacityHit() : m_theme->accentOpacityHit() * m_theme->itemOpacityDisabled());
     714            0 :     } else if (styleState.enabled && styleState.pressed) {
     715            0 :         backgroundColor = m_theme->buttonColor();
     716            0 :         backgroundColor.setAlphaF(m_theme->buttonOpacityHit());
     717            0 :     } else if (styleState.enabled && styleState.hovered) {
     718            0 :         backgroundColor = m_theme->buttonColor();
     719            0 :         backgroundColor.setAlphaF(m_theme->buttonOpacityHover());
     720              :     }
     721              : 
     722            0 :     painter->fillRect(rect, backgroundColor);
     723              : 
     724            0 :     if (m_theme->configuration()->isHighContrast()) {
     725            0 :         drawRoundedRect(painter, rect, 1, NO_FILL, QPen(m_theme->strokeColor(), m_theme->borderWidth()));
     726              :     }
     727              : 
     728            0 :     if (styleState.focused) {
     729            0 :         drawRoundedRect(painter, rect, 1, NO_FILL, QPen(m_theme->fontPrimaryColor(), m_theme->navCtrlBorderWidth()));
     730              :     }
     731            0 : }
     732              : 
     733            0 : void WidgetStyle::drawToolbarGrip(QPainter* painter, const QRect& rect, bool horizontal) const
     734              : {
     735            0 :     QColor gripColor(m_theme->fontPrimaryColor());
     736            0 :     gripColor.setAlphaF(0.5);
     737            0 :     painter->setPen(gripColor);
     738            0 :     painter->setFont(m_theme->iconsFont());
     739              : 
     740            0 :     int rotation = horizontal ? 0 : 90;
     741            0 :     QRect r = horizontal ? rect : QRect(rect.topLeft() + QPoint(0, -rect.width()),
     742            0 :                                         rect.size().transposed());
     743            0 :     painter->rotate(rotation);
     744            0 :     painter->drawText(r, Qt::AlignCenter, iconCodeToChar(IconCode::Code::TOOLBAR_GRIP));
     745            0 :     painter->rotate(-rotation);
     746            0 : }
        

Generated by: LCOV version 2.0-1