From 3493507c127806aebb855e89b9e0d2d8b02afa03 Mon Sep 17 00:00:00 2001 From: laserpants Date: Mon, 13 Jun 2016 02:19:14 +0300 Subject: [PATCH] implement Radio Button --- components/radiobutton.cpp | 287 ++++++++++++++++++++++++---- components/radiobutton.h | 25 ++- components/radiobutton_internal.cpp | 45 ++++- components/radiobutton_internal.h | 21 +- components/radiobutton_p.h | 18 +- examples/radiobuttonexamples.cpp | 22 ++- examples/raisedbuttonexamples.cpp | 2 +- 7 files changed, 372 insertions(+), 48 deletions(-) diff --git a/components/radiobutton.cpp b/components/radiobutton.cpp index be4716a..cfeb29f 100644 --- a/components/radiobutton.cpp +++ b/components/radiobutton.cpp @@ -1,13 +1,21 @@ #include "radiobutton.h" #include #include +#include +#include +#include +#include #include "radiobutton_p.h" #include "lib/rippleoverlay.h" +#include "lib/style.h" +#include "lib/ripple.h" RadioButtonPrivate::RadioButtonPrivate(RadioButton *q) : q_ptr(q), - checkedIcon(QIcon("../qt-material-widgets/ic_radio_button_checked_black_24px.svg")), - uncheckedIcon(QIcon("../qt-material-widgets/ic_radio_button_unchecked_black_24px.svg")) + checkedIcon(new RadioButtonIcon(QIcon("../qt-material-widgets/ic_radio_button_checked_black_24px.svg"))), + uncheckedIcon(new RadioButtonIcon(QIcon("../qt-material-widgets/ic_radio_button_unchecked_black_24px.svg"))), + iconSize(24), + useThemeColors(true) { } @@ -15,12 +23,79 @@ void RadioButtonPrivate::init() { Q_Q(RadioButton); + checkedIcon->setParent(q); + uncheckedIcon->setParent(q); + ripple = new RippleOverlay(q->parentWidget()); - iconWidget = new RadioButtonIcon(q); + machine = new QStateMachine(q); QFont f(q->font()); f.setPointSizeF(11); q->setFont(f); + + uncheckedState = new QState; + checkedState = new QState; + + machine->addState(uncheckedState); + machine->addState(checkedState); + + machine->setInitialState(uncheckedState); + + QSignalTransition *transition; + QPropertyAnimation *animation; + + transition = new QSignalTransition(q, SIGNAL(toggled(bool))); + transition->setTargetState(checkedState); + uncheckedState->addTransition(transition); + + animation = new QPropertyAnimation(checkedIcon, "iconSize"); + animation->setDuration(250); + transition->addAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "iconSize"); + animation->setDuration(250); + transition->addAnimation(animation); + + transition = new QSignalTransition(q, SIGNAL(toggled(bool))); + transition->setTargetState(uncheckedState); + checkedState->addTransition(transition); + + animation = new QPropertyAnimation(checkedIcon, "iconSize"); + animation->setDuration(250); + transition->addAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "iconSize"); + animation->setDuration(250); + transition->addAnimation(animation); + + assignAnimationProperties(); + updatePalette(); + + machine->start(); + + QCoreApplication::processEvents(); +} + +void RadioButtonPrivate::assignAnimationProperties() +{ + uncheckedState->assignProperty(checkedIcon, "iconSize", 0); + uncheckedState->assignProperty(uncheckedIcon, "iconSize", iconSize); + + checkedState->assignProperty(uncheckedIcon, "iconSize", 0); + checkedState->assignProperty(checkedIcon, "iconSize", iconSize); +} + +void RadioButtonPrivate::updatePalette() +{ + Q_Q(RadioButton); + + if (q->isEnabled()) { + checkedIcon->setColor(q->checkedColor()); + uncheckedIcon->setColor(q->uncheckedColor()); + } else { + checkedIcon->setColor(q->disabledColor()); + uncheckedIcon->setColor(q->disabledColor()); + } } RadioButton::RadioButton(QWidget *parent) @@ -28,21 +103,161 @@ RadioButton::RadioButton(QWidget *parent) d_ptr(new RadioButtonPrivate(this)) { d_func()->init(); - - //QPushButton *b = new QPushButton; - //b->setParent(this); - //b->setIcon(d_func()->checkedIcon); - //b->setIconSize(QSize(12, 12)); - - //QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; - //effect->setColor(Qt::blue); - //b->setGraphicsEffect(effect); } RadioButton::~RadioButton() { } +void RadioButton::setUseThemeColors(bool state) +{ + Q_D(RadioButton); + + d->useThemeColors = state; + d->updatePalette(); + update(); +} + +bool RadioButton::useThemeColors() const +{ + Q_D(const RadioButton); + + return d->useThemeColors; +} + +void RadioButton::setCheckedColor(const QColor &color) +{ + Q_D(RadioButton); + + d->checkedColor = color; + setUseThemeColors(false); +} + +QColor RadioButton::checkedColor() const +{ + Q_D(const RadioButton); + + if (d->useThemeColors || !d->checkedColor.isValid()) { + return Style::instance().themeColor("primary1"); + } else { + return d->checkedColor; + } +} + +void RadioButton::setUncheckedColor(const QColor &color) +{ + Q_D(RadioButton); + + d->uncheckedColor = color; + setUseThemeColors(false); +} + +QColor RadioButton::uncheckedColor() const +{ + Q_D(const RadioButton); + + if (d->useThemeColors || !d->uncheckedColor.isValid()) { + return Style::instance().themeColor("text"); + } else { + return d->uncheckedColor; + } +} + +void RadioButton::setTextColor(const QColor &color) +{ + Q_D(RadioButton); + + d->textColor = color; + setUseThemeColors(false); +} + +QColor RadioButton::textColor() const +{ + Q_D(const RadioButton); + + if (d->useThemeColors || !d->textColor.isValid()) { + return Style::instance().themeColor("text"); + } else { + return d->textColor; + } +} + +void RadioButton::setDisabledColor(const QColor &color) +{ + Q_D(RadioButton); + + d->disabledColor = color; + setUseThemeColors(false); +} + +QColor RadioButton::disabledColor() const +{ + Q_D(const RadioButton); + + if (d->useThemeColors || !d->disabledColor.isValid()) { + // Transparency will not work here, since we use this color to + // tint the icon using a QGraphicsColorizeEffect + return Style::instance().themeColor("accent3"); + } else { + return d->disabledColor; + } +} + +void RadioButton::setCheckedIcon(const QIcon &icon) +{ + Q_D(RadioButton); + + d->checkedIcon->setIcon(icon); +} + +QIcon RadioButton::checkedIcon() const +{ + Q_D(const RadioButton); + + return d->checkedIcon->icon(); +} + +void RadioButton::setUncheckedIcon(const QIcon &icon) +{ + Q_D(RadioButton); + + d->uncheckedIcon->setIcon(icon); +} + +QIcon RadioButton::uncheckedIcon() const +{ + Q_D(const RadioButton); + + return d->uncheckedIcon->icon(); +} + +void RadioButton::setIconSize(int size) +{ + Q_D(RadioButton); + + if (size > 38) { + size = 38; + qWarning() << "RadioButton::setIconSize: maximum allowed icon size is 38"; + } + + d->iconSize = size; + d->assignAnimationProperties(); + + if (isChecked()) { + d->checkedIcon->setIconSize(size); + } else { + d->uncheckedIcon->setIconSize(size); + } + update(); +} + +int RadioButton::iconSize() const +{ + Q_D(const RadioButton); + + return d->iconSize; +} + QSize RadioButton::sizeHint() const { QString s(text()); @@ -64,7 +279,8 @@ bool RadioButton::event(QEvent *event) case QEvent::Resize: case QEvent::Move: d->ripple->setGeometry(geometry().adjusted(-8, -8, 8, 8)); - d->iconWidget->setGeometry(geometry()); + d->checkedIcon->setGeometry(rect()); + d->uncheckedIcon->setGeometry(rect()); break; case QEvent::ParentChange: QWidget *widget; @@ -72,6 +288,16 @@ bool RadioButton::event(QEvent *event) d->ripple->setParent(widget); } break; + case QEvent::EnabledChange: + d->updatePalette(); + if (isChecked()) { + d->checkedIcon->setIconSize(d->iconSize); + d->uncheckedIcon->setIconSize(0); + } else { + d->checkedIcon->setIconSize(0); + d->uncheckedIcon->setIconSize(d->iconSize); + } + break; default: break; } @@ -80,39 +306,30 @@ bool RadioButton::event(QEvent *event) void RadioButton::mousePressEvent(QMouseEvent *event) { + Q_UNUSED(event) + + if (!isEnabled()) + return; + Q_D(RadioButton); - d->ripple->addRipple(QPoint(24, 24), 24); + Ripple *ripple = new Ripple(QPoint(24, 24)); + ripple->setRadiusEndValue(24); + ripple->setColor(isChecked() ? d->checkedIcon->color() : Qt::black); + d->ripple->addRipple(ripple); - QRadioButton::mousePressEvent(event); -} - -void RadioButton::mouseReleaseEvent(QMouseEvent *event) -{ - QRadioButton::mouseReleaseEvent(event); + setChecked(!isChecked()); } void RadioButton::paintEvent(QPaintEvent *event) { Q_UNUSED(event) - //Q_D(RadioButton); - QPainter painter(this); - //painter.save(); - //QRadioButton::paintEvent(event); - //painter.restore(); - - //painter.drawRect(rect().adjusted(0, 0, -1, -1)); - - // Icon - - //d->checkedIcon.paint(&painter, QRect(4, 4, 24, 24), Qt::AlignCenter, QIcon::Normal); - - //d->checkedIcon.pixmap() - - // Label + QPen pen; + pen.setColor(isEnabled() ? textColor() : disabledColor()); + painter.setPen(pen); painter.drawText(44, 21, text()); } diff --git a/components/radiobutton.h b/components/radiobutton.h index 117e24e..24bf010 100644 --- a/components/radiobutton.h +++ b/components/radiobutton.h @@ -13,12 +13,35 @@ public: explicit RadioButton(QWidget *parent = 0); ~RadioButton(); + void setUseThemeColors(bool state); + bool useThemeColors() const; + + void setCheckedColor(const QColor &color); + QColor checkedColor() const; + + void setUncheckedColor(const QColor &color); + QColor uncheckedColor() const; + + void setTextColor(const QColor &color); + QColor textColor() const; + + void setDisabledColor(const QColor &color); + QColor disabledColor() const; + + void setCheckedIcon(const QIcon &icon); + QIcon checkedIcon() const; + + void setUncheckedIcon(const QIcon &icon); + QIcon uncheckedIcon() const; + + void setIconSize(int size); + int iconSize() const; + QSize sizeHint() const; protected: bool event(QEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; const QScopedPointer d_ptr; diff --git a/components/radiobutton_internal.cpp b/components/radiobutton_internal.cpp index 76f9484..aec3c9c 100644 --- a/components/radiobutton_internal.cpp +++ b/components/radiobutton_internal.cpp @@ -1,21 +1,58 @@ #include "radiobutton_internal.h" #include +#include -RadioButtonIcon::RadioButtonIcon(QWidget *parent) - : QWidget(parent) +RadioButtonIcon::RadioButtonIcon(const QIcon &icon, QWidget *parent) + : QWidget(parent), + _icon(icon), + _iconSize(24), + _effect(new QGraphicsColorizeEffect) { + setAttribute(Qt::WA_TransparentForMouseEvents); + + setGraphicsEffect(_effect); } RadioButtonIcon::~RadioButtonIcon() { } +void RadioButtonIcon::setColor(const QColor &color) +{ + _effect->setColor(color); + update(); +} + +QColor RadioButtonIcon::color() const +{ + return _effect->color(); +} + +void RadioButtonIcon::setIconSize(int size) +{ + _iconSize = size; + update(); +} + +int RadioButtonIcon::iconSize() const +{ + return _iconSize; +} + void RadioButtonIcon::paintEvent(QPaintEvent *event) { Q_UNUSED(event) +#ifdef DEBUG_LAYOUT + QPainter debug(this); + debug.drawRect(rect().adjusted(0, 0, -1, -1)); +#endif + + if (0 == _iconSize) + return; + QPainter painter(this); - painter.setPen(Qt::red); - painter.drawRect(rect()); + const int p = (height() - _iconSize)/2; + _icon.paint(&painter, QRect(p, p, _iconSize, _iconSize)); } diff --git a/components/radiobutton_internal.h b/components/radiobutton_internal.h index 55f092c..6d07c8c 100644 --- a/components/radiobutton_internal.h +++ b/components/radiobutton_internal.h @@ -2,20 +2,39 @@ #define RADIOBUTTON_INTERNAL_H #include +#include + +class QGraphicsColorizeEffect; class RadioButtonIcon : public QWidget { Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(int iconSize READ iconSize WRITE setIconSize) + public: - RadioButtonIcon(QWidget *parent = 0); + RadioButtonIcon(const QIcon &icon, QWidget *parent = 0); ~RadioButtonIcon(); + inline void setIcon(const QIcon &icon) { _icon = icon; update(); } + inline QIcon icon() const { return _icon; } + + void setColor(const QColor &color); + QColor color() const; + + void setIconSize(int size); + int iconSize() const; + protected: void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; private: Q_DISABLE_COPY(RadioButtonIcon) + + QIcon _icon; + int _iconSize; + QGraphicsColorizeEffect *const _effect; }; #endif // RADIOBUTTON_INTERNAL_H diff --git a/components/radiobutton_p.h b/components/radiobutton_p.h index a119bda..91438e3 100644 --- a/components/radiobutton_p.h +++ b/components/radiobutton_p.h @@ -2,6 +2,7 @@ #define RADIOBUTTON_P_H #include +#include #include "radiobutton_internal.h" class RadioButton; @@ -17,11 +18,22 @@ public: void init(); + void assignAnimationProperties(); + void updatePalette(); + RadioButton *const q_ptr; RippleOverlay *ripple; - RadioButtonIcon *iconWidget; - QIcon checkedIcon; - QIcon uncheckedIcon; + RadioButtonIcon *checkedIcon; + RadioButtonIcon *uncheckedIcon; + QStateMachine *machine; + QState *uncheckedState; + QState *checkedState; + int iconSize; + QColor checkedColor; + QColor uncheckedColor; + QColor textColor; + QColor disabledColor; + bool useThemeColors; }; #endif // RADIOBUTTON_P_H diff --git a/examples/radiobuttonexamples.cpp b/examples/radiobuttonexamples.cpp index e1a0223..08efc16 100644 --- a/examples/radiobuttonexamples.cpp +++ b/examples/radiobuttonexamples.cpp @@ -17,13 +17,27 @@ RadioButtonExamples::RadioButtonExamples(QWidget *parent) RadioButton *radioButton1 = new RadioButton; RadioButton *radioButton2 = new RadioButton; RadioButton *radioButton3 = new RadioButton; + RadioButton *radioButton4 = new RadioButton; + RadioButton *radioButton5 = new RadioButton; + + radioButton3->setCheckedIcon(QIcon("../qt-material-widgets/ic_star_black_24px.svg")); + radioButton3->setUncheckedIcon(QIcon("../qt-material-widgets/ic_star_border_black_24px.svg")); + radioButton3->setIconSize(30); radioButton1->setText("Auto select"); radioButton2->setText("Option #2"); + radioButton4->setText("Disabled option"); + radioButton5->setText("Disabled option (checked)"); - bg->addButton(radioButton1); - bg->addButton(radioButton2); - bg->addButton(radioButton3); + radioButton4->setDisabled(true); + radioButton5->setDisabled(true); + radioButton5->setChecked(true); + + bg->addButton(radioButton1, 1); + bg->addButton(radioButton2, 2); + bg->addButton(radioButton3, 3); + bg->addButton(radioButton4, 4); + bg->addButton(radioButton5, 5); QWidget *widget = new QWidget; QVBoxLayout *vbl = new QVBoxLayout; @@ -38,6 +52,8 @@ RadioButtonExamples::RadioButtonExamples(QWidget *parent) vbl->addWidget(radioButton1); vbl->addWidget(radioButton2); vbl->addWidget(radioButton3); + vbl->addWidget(radioButton4); + vbl->addWidget(radioButton5); ExampleView *view = new ExampleView; view->setWidget(widget); diff --git a/examples/raisedbuttonexamples.cpp b/examples/raisedbuttonexamples.cpp index 196a900..53d93cf 100644 --- a/examples/raisedbuttonexamples.cpp +++ b/examples/raisedbuttonexamples.cpp @@ -15,7 +15,7 @@ RaisedButtonExamples::RaisedButtonExamples(QWidget *parent) raisedButton->setText("Press me!"); raisedButton->setMaximumWidth(408); - raisedButton->setDisabled(true); + //raisedButton->setDisabled(true); //raisedButton->setDisabled(true);