From 548af0261967fdc63b8ed5f94aeb11c1713cca97 Mon Sep 17 00:00:00 2001 From: Alfredo Monclus Date: Wed, 21 May 2025 08:00:54 -0600 Subject: [PATCH] Gui: fix light/dark switch when on classic (#21049) * Gui: fix: taskpanel light/dark switch when on classic * Gui: fix: taskpanel double icon * Apply suggestions from code review --------- Co-authored-by: Kacper Donat --- src/Gui/OverlayWidgets.cpp | 1 - src/Gui/QSint/actionpanel/actionbox.cpp | 2 + src/Gui/QSint/actionpanel/actiongroup.cpp | 25 +++- src/Gui/QSint/actionpanel/actiongroup.h | 8 ++ src/Gui/QSint/actionpanel/actionlabel.cpp | 36 ++++++ src/Gui/QSint/actionpanel/actionlabel.h | 3 + src/Gui/QSint/actionpanel/actionpanel.cpp | 15 ++- .../QSint/actionpanel/actionpanelscheme.cpp | 107 +++++------------- src/Gui/QSint/actionpanel/actionpanelscheme.h | 14 --- src/Gui/QSint/actionpanel/taskgroup_p.cpp | 10 ++ src/Gui/QSint/actionpanel/taskgroup_p.h | 1 + src/Gui/QSint/actionpanel/taskheader_p.cpp | 3 + .../overlay/Dark Theme + Dark Background.qss | 6 +- .../overlay/Dark Theme + Light Background.qss | 4 +- .../overlay/Light Theme + Dark Background.qss | 3 +- .../Light Theme + Light Background.qss | 3 +- 16 files changed, 136 insertions(+), 105 deletions(-) diff --git a/src/Gui/OverlayWidgets.cpp b/src/Gui/OverlayWidgets.cpp index fac07090f2..1ea7b88fc0 100644 --- a/src/Gui/OverlayWidgets.cpp +++ b/src/Gui/OverlayWidgets.cpp @@ -1273,7 +1273,6 @@ void OverlayTabWidget::setOverlayMode(QWidget *widget, OverlayOption option) && !qobject_cast(widget)) || qobject_cast(widget)) return; - if(widget != tabBar()) { if(OverlayParams::getDockOverlayAutoMouseThrough() && option == OverlayOption::ShowTab) { diff --git a/src/Gui/QSint/actionpanel/actionbox.cpp b/src/Gui/QSint/actionpanel/actionbox.cpp index ecd07440bc..7ef43ee9da 100644 --- a/src/Gui/QSint/actionpanel/actionbox.cpp +++ b/src/Gui/QSint/actionpanel/actionbox.cpp @@ -8,6 +8,7 @@ #include "actionbox.h" #include +#include namespace QSint { @@ -34,6 +35,7 @@ ActionBox::ActionBox(const QPixmap & icon, const QString & headerText, QWidget * void ActionBox::init(const QString &headerText) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + setFrameStyle(QFrame::NoFrame); auto *mainLayout = new QHBoxLayout(this); diff --git a/src/Gui/QSint/actionpanel/actiongroup.cpp b/src/Gui/QSint/actionpanel/actiongroup.cpp index dccafa22ec..f24e684a50 100644 --- a/src/Gui/QSint/actionpanel/actiongroup.cpp +++ b/src/Gui/QSint/actionpanel/actiongroup.cpp @@ -43,6 +43,8 @@ void ActionGroup::init(bool hasHeader) { m_foldStep = 0; myScheme = ActionPanelScheme::defaultScheme(); + setBackgroundRole(QPalette::Button); + setAutoFillBackground(true); auto *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -50,6 +52,13 @@ void ActionGroup::init(bool hasHeader) layout->addWidget(myHeader); + auto *separator = new QFrame(this); + separator->setFrameShape(QFrame::HLine); + separator->setFrameShadow(QFrame::Raised); + separator->setFixedHeight(separatorHeight); + separator->setContentsMargins(8, 0, 8, 0); + layout->addWidget(separator); + myGroup = new TaskGroup(this, hasHeader); layout->addWidget(myGroup); @@ -60,6 +69,14 @@ void ActionGroup::init(bool hasHeader) connect(myHeader, &TaskHeader::activated, this, &ActionGroup::showHide); } +void ActionGroup::setScheme(ActionPanelScheme *scheme) +{ + myScheme = scheme; + myHeader->setScheme(scheme); + myGroup->setScheme(scheme); + update(); +} + QBoxLayout* ActionGroup::groupLayout() { return myGroup->groupLayout(); @@ -122,14 +139,14 @@ void ActionGroup::processHide() { myDummy->hide(); myHeader->setFold(false); - setFixedHeight(myHeader->height()); + setFixedHeight(myHeader->height() + separatorHeight); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); return; } m_tempHeight -= m_foldDelta; myDummy->setFixedHeight(m_tempHeight); - setFixedHeight(myDummy->height() + myHeader->height()); + setFixedHeight(myDummy->height() + myHeader->height() + separatorHeight); QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processHide); } @@ -142,7 +159,7 @@ void ActionGroup::processShow() m_foldPixmap = QPixmap(); myGroup->show(); myHeader->setFold(true); - setFixedHeight(m_fullHeight + myHeader->height()); + setFixedHeight(m_fullHeight + myHeader->height() + separatorHeight); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); setMaximumHeight(QWIDGETSIZE_MAX); return; @@ -150,7 +167,7 @@ void ActionGroup::processShow() m_tempHeight += m_foldDelta; myDummy->setFixedHeight(m_tempHeight); - setFixedHeight(myDummy->height() + myHeader->height()); + setFixedHeight(myDummy->height() + myHeader->height() + separatorHeight); QTimer::singleShot(myScheme->groupFoldDelay, this, &ActionGroup::processShow); } diff --git a/src/Gui/QSint/actionpanel/actiongroup.h b/src/Gui/QSint/actionpanel/actiongroup.h index 3aa9551852..6a750b4cde 100644 --- a/src/Gui/QSint/actionpanel/actiongroup.h +++ b/src/Gui/QSint/actionpanel/actiongroup.h @@ -102,6 +102,11 @@ public: */ QBoxLayout* groupLayout(); + /** + * @brief Set the style of the widgets + */ + void setScheme(ActionPanelScheme *scheme); + /** * @brief Checks if the group is expandable. * @return `true` if the group is expandable, `false` otherwise. @@ -192,6 +197,9 @@ protected: TaskGroup *myGroup = nullptr; ///< The container for actions/widgets. QWidget *myDummy = nullptr; ///< Dummy widget for animation. ActionPanelScheme *myScheme = nullptr; ///< The color scheme. + +private: + const int separatorHeight = 1; }; } // namespace QSint diff --git a/src/Gui/QSint/actionpanel/actionlabel.cpp b/src/Gui/QSint/actionpanel/actionlabel.cpp index 35619027d0..ecdf455eec 100644 --- a/src/Gui/QSint/actionpanel/actionlabel.cpp +++ b/src/Gui/QSint/actionpanel/actionlabel.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include namespace QSint { @@ -68,4 +70,38 @@ QSize ActionLabel::minimumSizeHint() const return sizeHint(); } +void ActionLabel::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter painter(this); + + constexpr int elementSpacing = 5; + int iconSize = fontMetrics().height(); + QIcon icon = this->icon(); + QString text = this->text(); + + QRect contentRect = rect().adjusted(elementSpacing, elementSpacing, -elementSpacing, -elementSpacing); // Apply margins + + int currentX = contentRect.left(); + + if (!icon.isNull()) { + QPixmap pixmap = icon.pixmap(iconSize, iconSize); + QRect iconRect(currentX, contentRect.center().y() - iconSize / 2, iconSize, iconSize); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.drawPixmap(iconRect, pixmap); + currentX += iconSize + elementSpacing; + } + + if (!text.isEmpty()) { + QRect textRect(currentX, contentRect.top(), contentRect.right() - currentX, contentRect.height()); + + QFont font = this->font(); + font.setUnderline(underMouse()); + painter.setFont(font); + + painter.setRenderHint(QPainter::TextAntialiasing); + painter.drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, text); + } +} + } // namespace QSint diff --git a/src/Gui/QSint/actionpanel/actionlabel.h b/src/Gui/QSint/actionpanel/actionlabel.h index 2d52997b4b..53c0b6e37c 100644 --- a/src/Gui/QSint/actionpanel/actionlabel.h +++ b/src/Gui/QSint/actionpanel/actionlabel.h @@ -56,6 +56,9 @@ public: */ QSize minimumSizeHint() const override; +private: + void paintEvent(QPaintEvent *event) override; + protected: /** * @brief Initializes the ActionLabel. diff --git a/src/Gui/QSint/actionpanel/actionpanel.cpp b/src/Gui/QSint/actionpanel/actionpanel.cpp index e50b055395..4df2c68ecc 100644 --- a/src/Gui/QSint/actionpanel/actionpanel.cpp +++ b/src/Gui/QSint/actionpanel/actionpanel.cpp @@ -29,11 +29,18 @@ ActionPanel::ActionPanel(QWidget *parent) : void ActionPanel::setScheme(ActionPanelScheme *scheme) { - if (!scheme) return; - + if (!scheme) { + return; + } myScheme = scheme; - setStyleSheet(myScheme->actionStyle); - + // set scheme for children + QObjectList list(children()); + Q_FOREACH(QObject *obj, list) { + if (dynamic_cast(obj)) { + ((ActionGroup*)obj)->setScheme(scheme); + continue; + } + } update(); } diff --git a/src/Gui/QSint/actionpanel/actionpanelscheme.cpp b/src/Gui/QSint/actionpanel/actionpanelscheme.cpp index b041c83d60..10f9d321e5 100644 --- a/src/Gui/QSint/actionpanel/actionpanelscheme.cpp +++ b/src/Gui/QSint/actionpanel/actionpanelscheme.cpp @@ -11,60 +11,6 @@ namespace QSint { - -const QString ActionPanelScheme::minimumStyle = QStringLiteral( - "QSint--ActionGroup QFrame[class='header'] {" - "border: none;" - "}" - - "QSint--ActionGroup QToolButton[class='header'] {" - "border: none;" - "font-weight: bold;" - "text-align: center;" - "background: none;" - "}" - - "QSint--ActionGroup QToolButton[class='action'] {" - "border: none;" - "background: none;" - "}" - - "QSint--ActionGroup QToolButton[class='action']:hover {" - "text-decoration: underline;" - "}" - - "QSint--ActionGroup QFrame[class='content'][header='true'] {" - "border: none;" - "}" - -); - -QString ActionPanelScheme::systemStyle(const QPalette& p) -{ - const QColor& highlightColor = p.color(QPalette::Highlight); - QColor headerBackground = highlightColor.darker(150); - const QColor& groupBackground = p.color(QPalette::Button); - - QHash replacements; - replacements["headerBackground"] = headerBackground.name(); - replacements["groupBackground"] = groupBackground.name();; - - QString style = QStringLiteral( - "QSint--ActionGroup QFrame[class='header'] {" - "background-color: {headerBackground};" - "}" - "QSint--ActionGroup QFrame[class='content'] {" - "background-color: {groupBackground};" - "}" - ); - - for (auto it = replacements.begin(); it != replacements.end(); ++it) { - style.replace("{" + it.key() + "}", it.value()); - } - - return style; -} - QPixmap ActionPanelScheme::drawFoldIcon(const QPalette& palette, bool fold, bool hover) const { QSize bSize = headerButtonSize; @@ -82,13 +28,6 @@ QPixmap ActionPanelScheme::drawFoldIcon(const QPalette& palette, bool fold, bool qreal bot_Y = 0.40 * bSize.height(); qreal top_Y = 0.64 * bSize.height(); - if (hover) { - penWidth *= 1.8; - } - - painter.setBrush(Qt::NoBrush); - painter.setPen(QPen(palette.color(QPalette::HighlightedText), penWidth)); - QPolygon chevron; if (fold) { // Upward @@ -102,7 +41,20 @@ QPixmap ActionPanelScheme::drawFoldIcon(const QPalette& palette, bool fold, bool << QPoint(rig_X, bot_Y); } + painter.setBrush(Qt::NoBrush); + // Outline + QColor outlineColor = palette.color(QPalette::Button); + painter.setPen(QPen(outlineColor, penWidth * 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); painter.drawPolyline(chevron); + + // Chevron + if (hover) { + penWidth *= 1.8; + } + QColor mainColor = palette.color(QPalette::Text); + painter.setPen(QPen(mainColor, penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + painter.drawPolyline(chevron); + painter.end(); return QPixmap::fromImage(img); } @@ -115,24 +67,29 @@ ActionPanelScheme::ActionPanelScheme() QPalette p = QApplication::palette(); - headerButtonSize = QSize(16, 16); - headerButtonFold = drawFoldIcon(p, true, false); - headerButtonFoldOver = drawFoldIcon(p, true, true); - headerButtonUnfold = drawFoldIcon(p, false, false); - headerButtonUnfoldOver = drawFoldIcon(p, false, true); + int iconSize = fm.height(); + headerButtonSize = QSize(iconSize, iconSize); + builtinFold = drawFoldIcon(p, true, false); + builtinFoldOver = drawFoldIcon(p, true, true); + builtinUnfold = drawFoldIcon(p, false, false); + builtinUnfoldOver = drawFoldIcon(p, false, true); - builtinFold = headerButtonFold; - builtinFoldOver = headerButtonFoldOver; - builtinUnfold = headerButtonUnfold; - builtinUnfoldOver = headerButtonUnfoldOver; + if (qApp->styleSheet().isEmpty()) { + headerButtonFold = builtinFold; + headerButtonFoldOver = builtinFoldOver; + headerButtonUnfold = builtinUnfold; + headerButtonUnfoldOver = builtinUnfoldOver; + } else { + headerButtonFold = QPixmap(); + headerButtonFoldOver = QPixmap(); + headerButtonUnfold = QPixmap(); + headerButtonUnfoldOver = QPixmap(); + } groupFoldSteps = 20; groupFoldDelay = 15; groupFoldEffect = NoFolding; groupFoldThaw = true; - - actionStyle = minimumStyle + systemStyle(p); - builtinScheme = actionStyle; } ActionPanelScheme* ActionPanelScheme::defaultScheme() @@ -147,8 +104,6 @@ void ActionPanelScheme::clearActionStyle() headerButtonFoldOver = QPixmap(); headerButtonUnfold = QPixmap(); headerButtonUnfoldOver = QPixmap(); - - actionStyle = minimumStyle; } void ActionPanelScheme::restoreActionStyle() @@ -157,8 +112,6 @@ void ActionPanelScheme::restoreActionStyle() headerButtonFoldOver = builtinFoldOver; headerButtonUnfold = builtinUnfold; headerButtonUnfoldOver = builtinUnfoldOver; - - actionStyle = builtinScheme; } } // namespace QSint diff --git a/src/Gui/QSint/actionpanel/actionpanelscheme.h b/src/Gui/QSint/actionpanel/actionpanelscheme.h index 187dd98781..bfd590639c 100644 --- a/src/Gui/QSint/actionpanel/actionpanelscheme.h +++ b/src/Gui/QSint/actionpanel/actionpanelscheme.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -121,18 +120,6 @@ public: */ void restoreActionStyle(); - /** - * @brief Minimal CSS style. - */ - static const QString minimumStyle; - - /** - * @brief Generates a custom system style based on the palette. - * @param p The palette to use for generating the style. - * @return The generated style. - */ - QString systemStyle(const QPalette& p); - protected: /** * @brief Draws a fold/unfold icon. @@ -151,7 +138,6 @@ private: QPixmap builtinFoldOver; QPixmap builtinUnfold; QPixmap builtinUnfoldOver; - QString builtinScheme; }; } // namespace QSint diff --git a/src/Gui/QSint/actionpanel/taskgroup_p.cpp b/src/Gui/QSint/actionpanel/taskgroup_p.cpp index a516c3b981..661b1ae789 100644 --- a/src/Gui/QSint/actionpanel/taskgroup_p.cpp +++ b/src/Gui/QSint/actionpanel/taskgroup_p.cpp @@ -22,6 +22,8 @@ TaskGroup::TaskGroup(QWidget *parent, bool hasHeader) setProperty("class", "content"); setProperty("header", hasHeader ? "true" : "false"); + setScheme(ActionPanelScheme::defaultScheme()); + QVBoxLayout *vbl = new QVBoxLayout(); vbl->setContentsMargins(4, 4, 4, 4); vbl->setSpacing(0); @@ -30,6 +32,14 @@ TaskGroup::TaskGroup(QWidget *parent, bool hasHeader) setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); } +void TaskGroup::setScheme(ActionPanelScheme *scheme) +{ + if (scheme) { + myScheme = scheme; + update(); + } +} + bool TaskGroup::addActionLabel(ActionLabel *label, bool addToLayout, bool addStretch) { if (!label) { diff --git a/src/Gui/QSint/actionpanel/taskgroup_p.h b/src/Gui/QSint/actionpanel/taskgroup_p.h index 464af2c365..a7ba6786b7 100644 --- a/src/Gui/QSint/actionpanel/taskgroup_p.h +++ b/src/Gui/QSint/actionpanel/taskgroup_p.h @@ -25,6 +25,7 @@ class TaskGroup : public QFrame public: TaskGroup(QWidget *parent, bool hasHeader = false); + void setScheme(ActionPanelScheme *scheme); inline QBoxLayout* groupLayout() { diff --git a/src/Gui/QSint/actionpanel/taskheader_p.cpp b/src/Gui/QSint/actionpanel/taskheader_p.cpp index 01dd9ee4ed..524764d35d 100644 --- a/src/Gui/QSint/actionpanel/taskheader_p.cpp +++ b/src/Gui/QSint/actionpanel/taskheader_p.cpp @@ -35,6 +35,9 @@ TaskHeader::TaskHeader(const QIcon &icon, const QString &title, bool expandable, myTitle->setText(title); myTitle->setIcon(icon); myTitle->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + QFont font = myTitle->font(); + font.setBold(true); + myTitle->setFont(font); connect(myTitle, &ActionLabel::clicked, this, &TaskHeader::fold); diff --git a/src/Gui/Stylesheets/overlay/Dark Theme + Dark Background.qss b/src/Gui/Stylesheets/overlay/Dark Theme + Dark Background.qss index 975d7faf75..bb885553b2 100644 --- a/src/Gui/Stylesheets/overlay/Dark Theme + Dark Background.qss +++ b/src/Gui/Stylesheets/overlay/Dark Theme + Dark Background.qss @@ -1,6 +1,8 @@ -QFrame { +Gui--DockWnd--ReportOutput, +Gui--TaskView--TaskView { border: none; -} + } + QTabWidget::pane{ background-color: transparent; border: transparent; diff --git a/src/Gui/Stylesheets/overlay/Dark Theme + Light Background.qss b/src/Gui/Stylesheets/overlay/Dark Theme + Light Background.qss index c8c26d1038..6cbdfe9743 100644 --- a/src/Gui/Stylesheets/overlay/Dark Theme + Light Background.qss +++ b/src/Gui/Stylesheets/overlay/Dark Theme + Light Background.qss @@ -1,6 +1,8 @@ -QFrame { +Gui--DockWnd--ReportOutput, +Gui--TaskView--TaskView { border: none; } + QTabWidget::pane{ background-color: transparent; border: transparent; diff --git a/src/Gui/Stylesheets/overlay/Light Theme + Dark Background.qss b/src/Gui/Stylesheets/overlay/Light Theme + Dark Background.qss index a04348c897..e73a7334ff 100644 --- a/src/Gui/Stylesheets/overlay/Light Theme + Dark Background.qss +++ b/src/Gui/Stylesheets/overlay/Light Theme + Dark Background.qss @@ -1,4 +1,5 @@ -QFrame { +Gui--DockWnd--ReportOutput, +Gui--TaskView--TaskView { border: none; } diff --git a/src/Gui/Stylesheets/overlay/Light Theme + Light Background.qss b/src/Gui/Stylesheets/overlay/Light Theme + Light Background.qss index 9419ba7216..a1367a3bcc 100644 --- a/src/Gui/Stylesheets/overlay/Light Theme + Light Background.qss +++ b/src/Gui/Stylesheets/overlay/Light Theme + Light Background.qss @@ -1,4 +1,5 @@ -QFrame { +Gui--DockWnd--ReportOutput, +Gui--TaskView--TaskView { border: none; }