From 24d812ae8701beef768dd989dd63ca148e16675b Mon Sep 17 00:00:00 2001 From: Noah Davis Date: Fri, 30 Dec 2022 11:45:29 -0500 Subject: [PATCH] Change menu section style to label and inline separator Also make separator width the same as menu item content to keep the margins around the sections and separators similar on both sides. --- kstyle/breezemetrics.h | 1 - kstyle/breezepropertynames.cpp | 1 - kstyle/breezepropertynames.h | 1 - kstyle/breezestyle.cpp | 207 +++++++++++++-------------------- kstyle/breezestyle.h | 6 - 5 files changed, 80 insertions(+), 136 deletions(-) diff --git a/kstyle/breezemetrics.h b/kstyle/breezemetrics.h index e3ff0b6d..87752f80 100644 --- a/kstyle/breezemetrics.h +++ b/kstyle/breezemetrics.h @@ -53,7 +53,6 @@ struct Metrics { static constexpr int MenuItem_ExtraLeftMargin = 4; static constexpr int MenuItem_MarginHeight = 3; static constexpr int MenuItem_ItemSpacing = 4; - static constexpr int MenuItem_SeparatorPadding = 3; static constexpr int MenuItem_AcceleratorSpace = 16; static constexpr int MenuButton_IndicatorWidth = 20; diff --git a/kstyle/breezepropertynames.cpp b/kstyle/breezepropertynames.cpp index 9de69615..672028b2 100644 --- a/kstyle/breezepropertynames.cpp +++ b/kstyle/breezepropertynames.cpp @@ -14,7 +14,6 @@ const char PropertyNames::netWMForceShadow[] = "_KDE_NET_WM_FORCE_SHADOW"; const char PropertyNames::netWMSkipShadow[] = "_KDE_NET_WM_SKIP_SHADOW"; const char PropertyNames::sidePanelView[] = "_kde_side_panel_view"; const char PropertyNames::toolButtonAlignment[] = "_kde_toolButton_alignment"; -const char PropertyNames::menuTitle[] = "_breeze_toolButton_menutitle"; const char PropertyNames::alteredBackground[] = "_breeze_altered_background"; const char PropertyNames::highlightNeutral[] = "_kde_highlight_neutral"; const char PropertyNames::noSeparator[] = "_breeze_no_separator"; diff --git a/kstyle/breezepropertynames.h b/kstyle/breezepropertynames.h index 3125b0ab..64aa351f 100644 --- a/kstyle/breezepropertynames.h +++ b/kstyle/breezepropertynames.h @@ -16,7 +16,6 @@ struct PropertyNames { static const char netWMSkipShadow[]; static const char sidePanelView[]; static const char toolButtonAlignment[]; - static const char menuTitle[]; static const char alteredBackground[]; static const char highlightNeutral[]; static const char noSeparator[]; diff --git a/kstyle/breezestyle.cpp b/kstyle/breezestyle.cpp index 71ad6425..abdf5a54 100644 --- a/kstyle/breezestyle.cpp +++ b/kstyle/breezestyle.cpp @@ -3132,21 +3132,6 @@ QSize Style::pushButtonSizeFromContents(const QStyleOption *option, const QSize return expandSize(size, Metrics::Frame_FrameWidth); } -static QStyleOptionToolButton toolButtonMenuTitleOption(const QStyleOptionToolButton &option) -{ - QStyleOptionToolButton copy = QStyleOptionToolButton(option); - copy.font.setBold(false); - copy.state = Style::State_Enabled; - return copy; -} - -static QFont menuTitleFont(const QStyleOptionToolButton &option) -{ - auto font = option.font; - font.setPointSize(qRound(font.pointSize() * 1.1)); - return font; -} - //______________________________________________________________ QSize Style::toolButtonSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const { @@ -3159,12 +3144,6 @@ QSize Style::toolButtonSizeFromContents(const QStyleOption *option, const QSize // copy size QSize size = contentsSize; - if (isMenuTitle(widget)) { - const QStyleOptionToolButton copy = toolButtonMenuTitleOption(*toolButtonOption); - const QFontMetrics fm(menuTitleFont(copy)); - size.setWidth(std::max(size.width(), fm.size(Qt::TextShowMnemonic, copy.text).width())); - } - // get relevant state flags const State &state(option->state); const bool autoRaise(state & State_AutoRaise); @@ -3248,27 +3227,40 @@ QSize Style::menuItemSizeFromContents(const QStyleOption *option, const QSize &c } case QStyleOptionMenuItem::Separator: { - if (menuItemOption->text.isEmpty() && menuItemOption->icon.isNull()) { - return expandSize(QSize(0, 1), Metrics::MenuItem_MarginWidth, Metrics::MenuItem_SeparatorPadding); - - } else { - // build toolbutton option - const QStyleOptionToolButton toolButtonOption(separatorMenuItemOption(menuItemOption, widget)); - const QFontMetrics fm(menuTitleFont(toolButtonOption)); - - // make sure height is large enough for icon and text - const int iconWidth(menuItemOption->maxIconWidth); - const int textHeight(fm.height()); - if (!menuItemOption->icon.isNull()) { - size.setHeight(qMax(size.height(), iconWidth)); + // contentsSize for separators in QMenuPrivate::updateActionRects() is {2,2} + // We choose to override that. + // Have at least 1px for separator line. + int w = 1; + int h = 1; + + // If the menu item is a section, add width for text + // and make height the same as other menu items, plus extra top padding. + if (!menuItemOption->text.isEmpty()) { + auto font = menuItemOption->font; + font.setBold(true); + QFontMetrics fm(font); + QRect textRect = fm.boundingRect({}, Qt::TextSingleLine | Qt::TextHideMnemonic, + menuItemOption->text); + w = qMax(w, textRect.width()); + h = qMax(h, fm.height()); + + if (showIconsInMenuItems()) { + int iconWidth = menuItemOption->maxIconWidth; + if (isQtQuickControl(option, widget)) { + iconWidth = qMax(pixelMetric(PM_SmallIconSize, option, widget), iconWidth); + } + h = qMax(h, iconWidth); } - if (!menuItemOption->text.isEmpty()) { - size.setHeight(qMax(size.height(), textHeight)); - size.setWidth(qMax(size.width(), fm.boundingRect(menuItemOption->text).width())); + + if (menuItemOption->menuHasCheckableItems) { + h = qMax(h, Metrics::CheckBox_Size); } - return sizeFromContents(CT_ToolButton, &toolButtonOption, size, widget); + h = qMax(h, Metrics::MenuButton_IndicatorWidth); + h += Metrics::MenuItem_MarginHeight; // extra top padding } + + return {w + Metrics::MenuItem_MarginWidth * 2, h + Metrics::MenuItem_MarginHeight * 2}; } // for all other cases, return input @@ -5192,45 +5184,63 @@ bool Style::drawMenuItemControl(const QStyleOption *option, QPainter *painter, c return true; } - // copy rect and palette - const auto &rect(option->rect); - const auto &palette(option->palette); + // Size should be from sizeFromContents(CT_MenuItem, ...), + // but with width set to the widest menu item width. + // see QMenuPrivate::updateActionRects() + const auto &rect = option->rect; + const auto &palette = option->palette; + + // store state + const State &state = option->state; + const bool enabled = state & State_Enabled; + const bool selected = enabled && (state & State_Selected); + const bool sunken = enabled && (state & (State_Sunken)); + const bool reverseLayout = option->direction == Qt::RightToLeft; + const bool useStrongFocus = StyleConfigData::menuItemDrawStrongFocus(); // deal with separators if (menuItemOption->menuItemType == QStyleOptionMenuItem::Separator) { - // normal separator - if (menuItemOption->text.isEmpty() && menuItemOption->icon.isNull()) { - auto color(_helper->separatorColor(palette)); - QRect copy(rect); - - if (StyleConfigData::menuOpacity() < 100) { - color = _helper->alphaColor(palette.color(QPalette::WindowText), 0.25); - // don`t overlap with menu border - copy.adjust(1, 0, -1, 0); - } - - _helper->renderSeparator(painter, copy, color); - return true; - + auto contentsRect = rect.adjusted(Metrics::MenuItem_MarginWidth, 0, + -Metrics::MenuItem_MarginWidth, 0); + QColor separatorColor; + if (StyleConfigData::menuOpacity() < 100) { + separatorColor = _helper->alphaColor(palette.color(QPalette::WindowText), 0.25); } else { - /* - * separator can have a title and an icon - * in that case they are rendered as menu title buttons - */ - QStyleOptionToolButton copy(separatorMenuItemOption(menuItemOption, widget)); - renderMenuTitle(©, painter, widget); + separatorColor = _helper->separatorColor(palette); + } - return true; + if (!menuItemOption->text.isEmpty()) { // icon is ignored on purpose + contentsRect.adjust(0, Metrics::MenuItem_MarginHeight, 0, 0); + int flags = visualAlignment(option->direction, Qt::AlignLeft) | Qt::AlignVCenter; + flags |= Qt::TextSingleLine | Qt::TextHideMnemonic | Qt::TextDontClip; + auto font = menuItemOption->font; + font.setBold(true); + QFontMetrics fm(font); + auto textRect = fm.boundingRect(contentsRect, flags, menuItemOption->text); + const auto &labelColor = palette.color(QPalette::Current, QPalette::WindowText); + painter->setFont(font); + painter->setBrush(Qt::NoBrush); + // 0.7 is from Kirigami ListSectionHeader. + // Not using painter->setOpacity() because it disables text antialiasing. + painter->setPen(_helper->alphaColor(labelColor, 0.7)); + painter->drawText(textRect, flags, menuItemOption->text); + // Make spacing between label and line the same as + // the margin from the label to the left menu outline. + // This is intentionally not factored into CT_MenuItem + // size calculations since the line is meant to fill the + // remaining available area after the label has been rendered. + const qreal spacing = pixelMetric(PM_MenuHMargin, option, widget) + + Metrics::MenuItem_MarginWidth - 1; + if (reverseLayout) { + contentsRect.setRight(textRect.left() - spacing); + } else { + contentsRect.setLeft(textRect.right() + spacing); + } } - } - // store state - const State &state(option->state); - const bool enabled(state & State_Enabled); - const bool selected(enabled && (state & State_Selected)); - const bool sunken(enabled && (state & (State_Sunken))); - const bool reverseLayout(option->direction == Qt::RightToLeft); - const bool useStrongFocus(StyleConfigData::menuItemDrawStrongFocus()); + _helper->renderSeparator(painter, contentsRect, separatorColor); + return true; + } // render hover and focus if (useStrongFocus && (selected || sunken)) { @@ -6728,15 +6738,6 @@ bool Style::drawToolButtonComplexControl(const QStyleOptionComplex *option, QPai // detect buttons in tabbar, for which special rendering is needed const bool inTabBar(widget && qobject_cast(widget->parentWidget())); - const bool isMenuTitle(this->isMenuTitle(widget)); - if (isMenuTitle) { - // copy option to adjust state, and set font as not-bold - const QStyleOptionToolButton copy = toolButtonMenuTitleOption(*toolButtonOption); - - // render - renderMenuTitle(©, painter, widget); - return true; - } // copy option and alter palette QStyleOptionToolButton copy(*toolButtonOption); @@ -7441,24 +7442,6 @@ void Style::renderSpinBoxArrow(const SubControl &subControl, const QStyleOptionS _helper->renderArrow(painter, arrowRect, color, orientation); } -//______________________________________________________________________________ -void Style::renderMenuTitle(const QStyleOptionToolButton *option, QPainter *painter, const QWidget *) const -{ - // render a background rect for the title - const auto &palette(option->palette); - QColor bgColor = palette.color(QPalette::Text); - bgColor.setAlphaF(0.04); - const auto separatorColor(_helper->separatorColor(palette)); - _helper->renderMenuFrame(painter, option->rect, bgColor, separatorColor, true); - - // render text in the center of the rect - // icon is discarded on purpose - // make text the same size as a level 4 heading so it looks more title-ish - painter->setFont(menuTitleFont(*option)); - const auto contentsRect = insideMargin(option->rect, Metrics::MenuItem_MarginWidth, (isTabletMode() ? 2 : 1) * Metrics::MenuItem_MarginHeight); - drawItemText(painter, contentsRect, Qt::AlignCenter, palette, true, option->text, QPalette::WindowText); -} - //______________________________________________________________________________ qreal Style::dialAngle(const QStyleOptionSlider *sliderOption, int value) const { @@ -7833,36 +7816,6 @@ bool Style::showIconsOnPushButtons() const return g.readEntry("ShowIconsOnPushButtons", true); } -//____________________________________________________________________ -bool Style::isMenuTitle(const QWidget *widget) const -{ - // check widget - if (!widget) { - return false; - } - - // check property - const QVariant property(widget->property(PropertyNames::menuTitle)); - if (property.isValid()) { - return property.toBool(); - } - - // detect menu toolbuttons - QWidget *parent = widget->parentWidget(); - if (qobject_cast(parent)) { - foreach (auto child, parent->findChildren()) { - if (child->defaultWidget() != widget) { - continue; - } - const_cast(widget)->setProperty(PropertyNames::menuTitle, true); - return true; - } - } - - const_cast(widget)->setProperty(PropertyNames::menuTitle, false); - return false; -} - //____________________________________________________________________ bool Style::hasAlteredBackground(const QWidget *widget) const { diff --git a/kstyle/breezestyle.h b/kstyle/breezestyle.h index 10be6f3b..7f5ceab8 100644 --- a/kstyle/breezestyle.h +++ b/kstyle/breezestyle.h @@ -343,9 +343,6 @@ private: //* spinbox arrows void renderSpinBoxArrow(const SubControl &, const QStyleOptionSpinBox *, QPainter *, const QWidget *) const; - //* menu title - void renderMenuTitle(const QStyleOptionToolButton *, QPainter *, const QWidget *) const; - //* return dial angle based on option and value qreal dialAngle(const QStyleOptionSlider *, int) const; @@ -489,9 +486,6 @@ private: //* return true if icons should be shown on buttons bool showIconsOnPushButtons() const; - //* return true if passed widget is a menu title (KMenu::addTitle) - bool isMenuTitle(const QWidget *) const; - //* return true if passed widget is a menu title (KMenu::addTitle) bool hasAlteredBackground(const QWidget *) const;