From adc5153b5da1ccde5d835ba8c27d60b6579bec4c Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Mon, 19 Aug 2024 12:11:57 +0200 Subject: [PATCH] Core: Add Core::Switch New Component, implemented according to the design spec in Figma. Change-Id: I12e4882141a1f800b17175ca6dfc2f55517b7115 Reviewed-by: Cristian Adam --- src/plugins/coreplugin/welcomepagehelper.cpp | 89 +++++++++++++++++++ src/plugins/coreplugin/welcomepagehelper.h | 12 +++ .../tst_manual_widgets_components.cpp | 12 +++ 3 files changed, 113 insertions(+) diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp index b216df72402..94594229568 100644 --- a/src/plugins/coreplugin/welcomepagehelper.cpp +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -445,6 +445,95 @@ void ComboBox::paintEvent(QPaintEvent *) p.drawPixmap(iconPos, icon); } +constexpr TextFormat SwitchLabelTf + {Theme::Token_Text_Default, StyleHelper::UiElementLabelMedium}; +constexpr QSize switchTrackS(32, 16); + +Switch::Switch(const QString &text, QWidget *parent) + : QAbstractButton(parent) +{ + setText(text); + setCheckable(true); + setAttribute(Qt::WA_Hover); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + setLayoutDirection(Qt::RightToLeft); // Switch right, label left +} + +QSize Switch::sizeHint() const +{ + const QFontMetrics fm(SwitchLabelTf.font()); + const int textWidth = fm.horizontalAdvance(text()); + const int width = switchTrackS.width() + HGapS + textWidth; + return {width, ExPaddingGapM + SwitchLabelTf.lineHeight() + ExPaddingGapM}; +} + +QSize Switch::minimumSizeHint() const +{ + return switchTrackS; +} + +void Switch::paintEvent([[maybe_unused]] QPaintEvent *event) +{ + const bool ltr = layoutDirection() == Qt::LeftToRight; + const int trackX = ltr ? 0 : width() - switchTrackS.width(); + const int trackY = (height() - switchTrackS.height()) / 2; + const QRect trackR(QPoint(trackX, trackY), switchTrackS); + const int trackRounding = trackR.height() / 2; + const bool checkedEnabled = isChecked() && isEnabled(); + QPainter p(this); + + { // track + const bool hovered = underMouse(); + const QBrush fill = creatorColor(checkedEnabled ? (hovered ? Theme::Token_Accent_Subtle + : Theme::Token_Accent_Default) + : Theme::Token_Foreground_Subtle); + const QPen outline = checkedEnabled ? QPen(Qt::NoPen) + : creatorColor(hovered ? Theme::Token_Stroke_Muted + : Theme::Token_Stroke_Subtle); + drawCardBackground(&p, trackR, fill, outline, trackRounding); + } + { // track label + const QColor color = creatorColor(isEnabled() ? (isChecked() ? Theme::Token_Basic_White + : Theme::Token_Text_Muted) + : Theme::Token_Text_Subtle); + const int labelS = 6; + const int labelY = (height() - labelS) / 2; + if (isChecked()) { + const QRect onLabelR(trackX + 8, labelY, 1, labelS); + p.fillRect(onLabelR, color); + } else { + const QRect offLabelR(trackX + switchTrackS.width() - trackRounding - labelS / 2 - 1, + labelY, labelS, labelS); + drawCardBackground(&p, offLabelR, Qt::NoBrush, color, labelS / 2); + } + } + { // knob + const int thumbPadding = checkedEnabled ? 3 : 2; + const int thumbH = switchTrackS.height() - thumbPadding - thumbPadding; + const int thumbW = isDown() ? (checkedEnabled ? 17 : 19) + : thumbH; + const int thumbRounding = thumbH / 2; + const int thumbX = trackX + (isChecked() ? switchTrackS.width() - thumbW - thumbPadding + : thumbPadding); + const QRect thumbR(thumbX, trackY + thumbPadding, thumbW, thumbH); + const QBrush fill = creatorColor(isEnabled() ? Theme::Token_Basic_White + : Theme::Token_Foreground_Default); + const QPen outline = checkedEnabled ? QPen(Qt::NoPen) + : creatorColor(Theme::Token_Stroke_Subtle); + drawCardBackground(&p, thumbR, fill, outline, thumbRounding); + } + { // switch text label + const int switchAndGapWidth = switchTrackS.width() + HGapS; + const QRect textR(ltr ? switchAndGapWidth : 0, 0, width() - switchAndGapWidth, + trackY + switchTrackS.height()); + p.setFont(SwitchLabelTf.font()); + p.setPen(isEnabled() ? SwitchLabelTf.color() : creatorColor(Theme::Token_Text_Subtle)); + const QString elidedLabel = + p.fontMetrics().elidedText(text(), Qt::ElideRight, textR.width()); + p.drawText(textR, SwitchLabelTf.drawTextFlags, elidedLabel); + } +} + GridView::GridView(QWidget *parent) : QListView(parent) { diff --git a/src/plugins/coreplugin/welcomepagehelper.h b/src/plugins/coreplugin/welcomepagehelper.h index 82a51c40e16..1fb6ff08874 100644 --- a/src/plugins/coreplugin/welcomepagehelper.h +++ b/src/plugins/coreplugin/welcomepagehelper.h @@ -141,6 +141,18 @@ protected: void leaveEvent(QEvent *event) override; }; +class CORE_EXPORT Switch : public QAbstractButton +{ +public: + explicit Switch(const QString &text, QWidget *parent = nullptr); + + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + +protected: + void paintEvent(QPaintEvent *event) override; +}; + class CORE_EXPORT GridView : public QListView { public: diff --git a/tests/manual/widgets/components/tst_manual_widgets_components.cpp b/tests/manual/widgets/components/tst_manual_widgets_components.cpp index 8698c850da9..7e8ea63d34b 100644 --- a/tests/manual/widgets/components/tst_manual_widgets_components.cpp +++ b/tests/manual/widgets/components/tst_manual_widgets_components.cpp @@ -17,6 +17,11 @@ QWidget *widgets() const QStringList content = QColor::colorNames(); comboBox->addItems(content.first(8)); + auto switchOn = new Core::Switch("Qt::RightToLeft"); + switchOn->setChecked(true); + auto switchOff = new Core::Switch("Qt::LeftToRight"); + switchOff->setLayoutDirection(Qt::LeftToRight); + using namespace Layouting; Column { Group { @@ -50,6 +55,13 @@ QWidget *widgets() comboBox, }, }, + Group { + title("Core::Switch"), + Column { + switchOn, + switchOff, + }, + }, }.attachTo(widget); return widget;