From fcde226168dbb93174ce16fcd10f017b4ae8f1d2 Mon Sep 17 00:00:00 2001 From: laserpants Date: Tue, 14 Jun 2016 02:27:37 +0300 Subject: [PATCH] refactor checkbox and radiobutton code --- components/checkbox.cpp | 359 ++++-------------------------- components/checkbox.h | 48 +---- components/checkbox_internal.cpp | 3 +- components/checkbox_p.h | 32 +-- components/radiobutton.cpp | 360 +++++-------------------------- components/radiobutton.h | 36 +--- components/radiobutton_p.h | 27 +-- components/raisedbutton.cpp | 4 +- examples/checkboxexamples.cpp | 12 +- examples/radiobuttonexamples.cpp | 1 - lib/checkable.cpp | 307 ++++++++++++++++++++++++++ lib/checkable.h | 55 +++++ lib/checkable_internal.cpp | 82 +++++++ lib/checkable_internal.h | 45 ++++ lib/checkable_p.h | 42 ++++ qt-material-widgets.pro | 9 +- 16 files changed, 674 insertions(+), 748 deletions(-) create mode 100644 lib/checkable.cpp create mode 100644 lib/checkable.h create mode 100644 lib/checkable_internal.cpp create mode 100644 lib/checkable_internal.h create mode 100644 lib/checkable_p.h diff --git a/components/checkbox.cpp b/components/checkbox.cpp index a96cd28..df1918a 100644 --- a/components/checkbox.cpp +++ b/components/checkbox.cpp @@ -1,347 +1,80 @@ #include "checkbox.h" -#include +#include #include #include +#include #include #include #include #include "checkbox_p.h" -#include "lib/rippleoverlay.h" -#include "lib/style.h" -#include "lib/ripple.h" -CheckboxPrivate::CheckboxPrivate(Checkbox *q) - : q_ptr(q), - checkedIcon(new CheckboxIcon(QIcon("../qt-material-widgets/ic_check_box_black_24px.svg"))), - uncheckedIcon(new CheckboxIcon(QIcon("../qt-material-widgets/ic_check_box_outline_blank_black_24px.svg"))), - iconSize(24), - useThemeColors(true) +CheckBoxPrivate::CheckBoxPrivate(CheckBox *q) + : CheckablePrivate(q) { } -void CheckboxPrivate::init() +CheckBoxPrivate::~CheckBoxPrivate() { - Q_Q(Checkbox); +} - q->setCheckable(true); - - uncheckedIcon->setParent(q); - checkedIcon->setParent(q); - - ripple = new RippleOverlay(q->parentWidget()); - machine = new QStateMachine(q); - - QFont f(q->font()); - f.setPointSizeF(11); - q->setFont(f); - - uncheckedState = new QState; - transitionState = new QState; - checkedState = new QState; - - machine->addState(uncheckedState); - machine->addState(transitionState); - machine->addState(checkedState); - - machine->setInitialState(uncheckedState); - - QSignalTransition *transition; +void CheckBoxPrivate::init() +{ QPropertyAnimation *animation; - transition = new QSignalTransition(q, SIGNAL(toggled(bool))); - transition->setTargetState(transitionState); - uncheckedState->addTransition(transition); - - transition = new QSignalTransition(transitionState, SIGNAL(propertiesAssigned())); - transition->setTargetState(checkedState); - transitionState->addTransition(transition); - animation = new QPropertyAnimation(checkedIcon, "iconSize"); + animation->setStartValue(0); + animation->setEndValue(24); animation->setDuration(250); - transition->addAnimation(animation); + uncheckedTransition->addAnimation(animation); - transition = new QSignalTransition(q, SIGNAL(toggled(bool))); - transition->setTargetState(uncheckedState); - checkedState->addTransition(transition); + animation = new QPropertyAnimation(uncheckedIcon, "color"); + animation->setDuration(250); + uncheckedTransition->addAnimation(animation); + + // + + animation = new QPropertyAnimation(uncheckedIcon, "color"); + animation->setDuration(550); + checkedTransition->addAnimation(animation); animation = new QPropertyAnimation(checkedIcon, "opacity"); - animation->setDuration(350); - transition->addAnimation(animation); + animation->setDuration(450); + checkedTransition->addAnimation(animation); - animation = new QPropertyAnimation(checkedIcon, "iconSize"); - animation->setDuration(1250); - transition->addAnimation(animation); + // - q->assignAnimationProperties(); - updatePalette(); + checkedState->assignProperty(checkedIcon, "iconSize", 24); + checkedState->assignProperty(checkedIcon, "opacity", 1); + uncheckedState->assignProperty(checkedIcon, "opacity", 0); + disabledCheckedState->assignProperty(checkedIcon, "opacity", 1); + disabledUncheckedState->assignProperty(uncheckedIcon, "opacity", 1); +} - machine->start(); +CheckBox::CheckBox(QWidget *parent) + : Checkable(*new CheckBoxPrivate(this), parent) +{ + Q_D(CheckBox); + d->init(); + + CheckBox::assignProperties(); + + d->machine->start(); QCoreApplication::processEvents(); } -void CheckboxPrivate::updatePalette() -{ - Q_Q(Checkbox); - - if (q->isEnabled()) { - checkedIcon->setColor(q->checkedColor()); - uncheckedIcon->setColor(q->uncheckedColor()); - } else { - checkedIcon->setColor(q->disabledColor()); - uncheckedIcon->setColor(q->disabledColor()); - } -} - -Checkbox::Checkbox(QWidget *parent) - : QAbstractButton(parent), - d_ptr(new CheckboxPrivate(this)) -{ - d_func()->init(); -} - -Checkbox::~Checkbox() +CheckBox::~CheckBox() { } -void Checkbox::setUseThemeColors(bool state) +void CheckBox::assignProperties() { - Q_D(Checkbox); + Q_D(CheckBox); - d->useThemeColors = state; - d->updatePalette(); - update(); -} - -bool Checkbox::useThemeColors() const -{ - Q_D(const Checkbox); - - return d->useThemeColors; -} - -void Checkbox::setCheckedColor(const QColor &color) -{ - Q_D(Checkbox); - - d->checkedColor = color; - setUseThemeColors(false); -} - -QColor Checkbox::checkedColor() const -{ - Q_D(const Checkbox); - - if (d->useThemeColors || !d->checkedColor.isValid()) { - return Style::instance().themeColor("primary1"); - } else { - return d->checkedColor; - } -} - -void Checkbox::setUncheckedColor(const QColor &color) -{ - Q_D(Checkbox); - - d->uncheckedColor = color; - setUseThemeColors(false); -} - -QColor Checkbox::uncheckedColor() const -{ - Q_D(const Checkbox); - - if (d->useThemeColors || !d->uncheckedColor.isValid()) { - return Style::instance().themeColor("text"); - } else { - return d->uncheckedColor; - } -} - -void Checkbox::setTextColor(const QColor &color) -{ - Q_D(Checkbox); - - d->textColor = color; - setUseThemeColors(false); -} - -QColor Checkbox::textColor() const -{ - Q_D(const Checkbox); - - if (d->useThemeColors || !d->textColor.isValid()) { - return Style::instance().themeColor("text"); - } else { - return d->textColor; - } -} - -void Checkbox::setDisabledColor(const QColor &color) -{ - Q_D(Checkbox); - - d->disabledColor = color; - setUseThemeColors(false); -} - -QColor Checkbox::disabledColor() const -{ - Q_D(const Checkbox); - - 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 Checkbox::setCheckedIcon(const QIcon &icon) -{ - Q_D(Checkbox); - - d->checkedIcon->setIcon(icon); -} - -QIcon Checkbox::checkedIcon() const -{ - Q_D(const Checkbox); - - return d->checkedIcon->icon(); -} - -void Checkbox::setUncheckedIcon(const QIcon &icon) -{ - Q_D(Checkbox); - - d->uncheckedIcon->setIcon(icon); -} - -QIcon Checkbox::uncheckedIcon() const -{ - Q_D(const Checkbox); - - return d->uncheckedIcon->icon(); -} - -void Checkbox::setIconSize(int size) -{ - Q_D(Checkbox); - - if (size > 38) { - size = 38; - qWarning() << "Checkbox::setIconSize: maximum allowed icon size is 38"; - } - - d->iconSize = size; - assignAnimationProperties(); - - if (isChecked()) { - d->checkedIcon->setIconSize(size); - d->uncheckedIcon->setIconSize(0); - } else { - d->uncheckedIcon->setIconSize(size); - d->checkedIcon->setIconSize(0); - } - update(); -} - -int Checkbox::iconSize() const -{ - Q_D(const Checkbox); - - return d->iconSize; -} - -QSize Checkbox::sizeHint() const -{ - QString s(text()); - if (s.isEmpty()) - return QSize(32, 32); - - QFontMetrics fm = fontMetrics(); - QSize sz = fm.size(Qt::TextShowMnemonic, s); - - return QSize(sz.width() + 44, 32); -} - -bool Checkbox::event(QEvent *event) -{ - Q_D(Checkbox); - - switch (event->type()) - { - case QEvent::Resize: - case QEvent::Move: - d->ripple->setGeometry(geometry().adjusted(-8, -8, 8, 8)); - d->checkedIcon->setGeometry(rect()); - d->uncheckedIcon->setGeometry(rect()); - break; - case QEvent::ParentChange: - QWidget *widget; - if ((widget = parentWidget())) { - 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; - } - return QAbstractButton::event(event); -} - -void Checkbox::mousePressEvent(QMouseEvent *event) -{ - Q_UNUSED(event) - - if (!isEnabled()) - return; - - Q_D(Checkbox); - - Ripple *ripple = new Ripple(QPoint(24, 24)); - ripple->setRadiusEndValue(24); - ripple->setColor(isChecked() ? d->checkedIcon->color() : Qt::black); - d->ripple->addRipple(ripple); - - setChecked(!isChecked()); -} - -void Checkbox::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event) - - QPainter painter(this); - - QPen pen; - pen.setColor(isEnabled() ? textColor() : disabledColor()); - painter.setPen(pen); - - painter.drawText(44, 21, text()); -} - -void Checkbox::assignAnimationProperties() -{ - Q_D(Checkbox); - - d->uncheckedState->assignProperty(d->checkedIcon, "opacity", 0); - d->uncheckedState->assignProperty(d->checkedIcon, "iconSize", d->iconSize); - d->uncheckedState->assignProperty(d->uncheckedIcon, "color", uncheckedColor()); - - d->transitionState->assignProperty(d->checkedIcon, "iconSize", 0); - - d->checkedState->assignProperty(d->checkedIcon, "opacity", 1); - d->checkedState->assignProperty(d->checkedIcon, "iconSize", d->iconSize); + d->checkedState->assignProperty(d->checkedIcon, "color", checkedColor()); d->checkedState->assignProperty(d->uncheckedIcon, "color", checkedColor()); + d->uncheckedState->assignProperty(d->uncheckedIcon, "color", uncheckedColor()); + d->uncheckedState->assignProperty(d->uncheckedIcon, "color", uncheckedColor()); + d->disabledUncheckedState->assignProperty(d->uncheckedIcon, "color", disabledColor()); + d->disabledCheckedState->assignProperty(d->checkedIcon, "color", disabledColor()); } diff --git a/components/checkbox.h b/components/checkbox.h index 3802318..f4a285f 100644 --- a/components/checkbox.h +++ b/components/checkbox.h @@ -1,56 +1,24 @@ #ifndef CHECKBOX_H #define CHECKBOX_H -#include +#include "lib/checkable.h" -class CheckboxPrivate; +class CheckBoxPrivate; -class Checkbox : public QAbstractButton +class CheckBox : public Checkable { Q_OBJECT public: - explicit Checkbox(QWidget *parent = 0); - ~Checkbox(); - - 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; + explicit CheckBox(QWidget *parent = 0); + ~CheckBox(); protected: - bool event(QEvent *event) Q_DECL_OVERRIDE; - void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; - void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; - - virtual void assignAnimationProperties(); - - const QScopedPointer d_ptr; + void assignProperties() Q_DECL_OVERRIDE; private: - Q_DISABLE_COPY(Checkbox) - Q_DECLARE_PRIVATE(Checkbox) + Q_DISABLE_COPY(CheckBox) + Q_DECLARE_PRIVATE(CheckBox) }; #endif // CHECKBOX_H diff --git a/components/checkbox_internal.cpp b/components/checkbox_internal.cpp index 728ec49..90da8d9 100644 --- a/components/checkbox_internal.cpp +++ b/components/checkbox_internal.cpp @@ -1,5 +1,6 @@ #include "checkbox_internal.h" #include +#include #include CheckboxIcon::CheckboxIcon(const QIcon &icon, QWidget *parent) @@ -71,7 +72,7 @@ void CheckboxIcon::paintEvent(QPaintEvent *event) painter.setOpacity(_opacity); const qreal p = static_cast((height())-_iconSize)/2; - const qreal z = static_cast(_iconSize)/24; + const qreal z = _iconSize/24; QTransform t; t.translate(p, p); diff --git a/components/checkbox_p.h b/components/checkbox_p.h index 4cca759..09a2dc0 100644 --- a/components/checkbox_p.h +++ b/components/checkbox_p.h @@ -1,38 +1,18 @@ #ifndef CHECKBOX_P_H #define CHECKBOX_P_H -#include -#include -#include "checkbox_internal.h" +#include "lib/checkable_p.h" -class Checkbox; -class RippleOverlay; - -class CheckboxPrivate +class CheckBoxPrivate : public CheckablePrivate { - Q_DISABLE_COPY(CheckboxPrivate) - Q_DECLARE_PUBLIC(Checkbox) + Q_DISABLE_COPY(CheckBoxPrivate) + Q_DECLARE_PUBLIC(CheckBox) public: - CheckboxPrivate(Checkbox *q); + CheckBoxPrivate(CheckBox *q); + ~CheckBoxPrivate(); void init(); - void updatePalette(); - - Checkbox *const q_ptr; - RippleOverlay *ripple; - CheckboxIcon *checkedIcon; - CheckboxIcon *uncheckedIcon; - QStateMachine *machine; - QState *uncheckedState; - QState *transitionState; - QState *checkedState; - int iconSize; - QColor checkedColor; - QColor uncheckedColor; - QColor textColor; - QColor disabledColor; - bool useThemeColors; }; #endif // CHECKBOX_P_H diff --git a/components/radiobutton.cpp b/components/radiobutton.cpp index 28d6816..d1361f4 100644 --- a/components/radiobutton.cpp +++ b/components/radiobutton.cpp @@ -1,21 +1,19 @@ #include "radiobutton.h" -#include +#include #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(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) + : CheckablePrivate(q) +{ +} + +RadioButtonPrivate::~RadioButtonPrivate() { } @@ -23,318 +21,76 @@ void RadioButtonPrivate::init() { Q_Q(RadioButton); - q->setCheckable(true); q->setAutoExclusive(true); - checkedIcon->setParent(q); - uncheckedIcon->setParent(q); + q->setCheckedIcon(QIcon("../qt-material-widgets/ic_radio_button_checked_black_24px.svg")); + q->setUncheckedIcon(QIcon("../qt-material-widgets/ic_radio_button_unchecked_black_24px.svg")); - ripple = new RippleOverlay(q->parentWidget()); - 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); + uncheckedState->assignProperty(uncheckedIcon, "iconSize", 24); + disabledUncheckedState->assignProperty(checkedIcon, "iconSize", 0); + disabledUncheckedState->assignProperty(uncheckedIcon, "iconSize", 24); checkedState->assignProperty(uncheckedIcon, "iconSize", 0); - checkedState->assignProperty(checkedIcon, "iconSize", iconSize); -} + checkedState->assignProperty(checkedIcon, "iconSize", 24); + disabledCheckedState->assignProperty(uncheckedIcon, "iconSize", 0); + disabledCheckedState->assignProperty(checkedIcon, "iconSize", 24); -void RadioButtonPrivate::updatePalette() -{ - Q_Q(RadioButton); + uncheckedState->assignProperty(checkedIcon, "opacity", 0); + uncheckedState->assignProperty(uncheckedIcon, "opacity", 1); + checkedState->assignProperty(uncheckedIcon, "opacity", 0); + checkedState->assignProperty(checkedIcon, "opacity", 1); - if (q->isEnabled()) { - checkedIcon->setColor(q->checkedColor()); - uncheckedIcon->setColor(q->uncheckedColor()); - } else { - checkedIcon->setColor(q->disabledColor()); - uncheckedIcon->setColor(q->disabledColor()); - } + disabledUncheckedState->assignProperty(uncheckedIcon, "opacity", 1); + disabledCheckedState->assignProperty(checkedIcon, "opacity", 1); + + QPropertyAnimation *animation; + + animation = new QPropertyAnimation(checkedIcon, "iconSize"); + animation->setDuration(250); + machine->addDefaultAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "iconSize"); + animation->setDuration(250); + machine->addDefaultAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "opacity"); + animation->setDuration(250); + machine->addDefaultAnimation(animation); + + animation = new QPropertyAnimation(checkedIcon, "opacity"); + animation->setDuration(250); + machine->addDefaultAnimation(animation); } RadioButton::RadioButton(QWidget *parent) - : QAbstractButton(parent), - d_ptr(new RadioButtonPrivate(this)) + : Checkable(*new RadioButtonPrivate(this), parent) { - d_func()->init(); + Q_D(RadioButton); + + d->init(); + + RadioButton::assignProperties(); + + d->machine->start(); + QCoreApplication::processEvents(); } RadioButton::~RadioButton() { } -void RadioButton::setUseThemeColors(bool state) +void RadioButton::assignProperties() { 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); - d->uncheckedIcon->setIconSize(0); - } else { - d->uncheckedIcon->setIconSize(size); - d->checkedIcon->setIconSize(0); - } - update(); -} - -int RadioButton::iconSize() const -{ - Q_D(const RadioButton); - - return d->iconSize; -} - -QSize RadioButton::sizeHint() const -{ - QString s(text()); - if (s.isEmpty()) - return QSize(32, 32); - - QFontMetrics fm = fontMetrics(); - QSize sz = fm.size(Qt::TextShowMnemonic, s); - - return QSize(sz.width() + 44, 32); -} - -bool RadioButton::event(QEvent *event) -{ - Q_D(RadioButton); - - switch (event->type()) - { - case QEvent::Resize: - case QEvent::Move: - d->ripple->setGeometry(geometry().adjusted(-8, -8, 8, 8)); - d->checkedIcon->setGeometry(rect()); - d->uncheckedIcon->setGeometry(rect()); - break; - case QEvent::ParentChange: - QWidget *widget; - if ((widget = parentWidget())) { - 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; - } - return QAbstractButton::event(event); -} - -void RadioButton::mousePressEvent(QMouseEvent *event) -{ - Q_UNUSED(event) - - if (!isEnabled()) - return; - - Q_D(RadioButton); - - Ripple *ripple = new Ripple(QPoint(24, 24)); - ripple->setRadiusEndValue(24); - ripple->setColor(isChecked() ? d->checkedIcon->color() : Qt::black); - d->ripple->addRipple(ripple); - - setChecked(!isChecked()); -} - -void RadioButton::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event) - - QPainter painter(this); - - QPen pen; - pen.setColor(isEnabled() ? textColor() : disabledColor()); - painter.setPen(pen); - - painter.drawText(44, 21, text()); + d->disabledUncheckedState->assignProperty(d->uncheckedIcon, "color", disabledColor()); + d->disabledUncheckedState->assignProperty(d->checkedIcon, "color", disabledColor()); + d->disabledCheckedState->assignProperty(d->checkedIcon, "color", disabledColor()); + d->disabledCheckedState->assignProperty(d->uncheckedIcon, "color", disabledColor()); + + d->uncheckedState->assignProperty(d->uncheckedIcon, "color", uncheckedColor()); + d->uncheckedState->assignProperty(d->checkedIcon, "color", checkedColor()); + d->checkedState->assignProperty(d->uncheckedIcon, "color", uncheckedColor()); + d->checkedState->assignProperty(d->checkedIcon, "color", checkedColor()); } diff --git a/components/radiobutton.h b/components/radiobutton.h index 92f2c7a..61f0a21 100644 --- a/components/radiobutton.h +++ b/components/radiobutton.h @@ -1,11 +1,11 @@ #ifndef RADIOBUTTON_H #define RADIOBUTTON_H -#include +#include "lib/checkable.h" class RadioButtonPrivate; -class RadioButton : public QAbstractButton +class RadioButton : public Checkable { Q_OBJECT @@ -13,38 +13,8 @@ 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 paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; - - const QScopedPointer d_ptr; + void assignProperties() Q_DECL_OVERRIDE; private: Q_DISABLE_COPY(RadioButton) diff --git a/components/radiobutton_p.h b/components/radiobutton_p.h index 91438e3..5273259 100644 --- a/components/radiobutton_p.h +++ b/components/radiobutton_p.h @@ -1,39 +1,18 @@ #ifndef RADIOBUTTON_P_H #define RADIOBUTTON_P_H -#include -#include -#include "radiobutton_internal.h" +#include "lib/checkable_p.h" -class RadioButton; -class RippleOverlay; - -class RadioButtonPrivate +class RadioButtonPrivate : public CheckablePrivate { Q_DISABLE_COPY(RadioButtonPrivate) Q_DECLARE_PUBLIC(RadioButton) public: RadioButtonPrivate(RadioButton *q); + ~RadioButtonPrivate(); void init(); - - void assignAnimationProperties(); - void updatePalette(); - - RadioButton *const q_ptr; - RippleOverlay *ripple; - 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/components/raisedbutton.cpp b/components/raisedbutton.cpp index 5890312..07c35eb 100644 --- a/components/raisedbutton.cpp +++ b/components/raisedbutton.cpp @@ -100,9 +100,7 @@ void RaisedButtonPrivate::init() RaisedButton::RaisedButton(QWidget *parent) : FlatButton(*new RaisedButtonPrivate(this), parent) { - Q_D(RaisedButton); - - d->init(); + d_func()->init(); setMinimumHeight(42); } diff --git a/examples/checkboxexamples.cpp b/examples/checkboxexamples.cpp index bb7c51a..a5816e6 100644 --- a/examples/checkboxexamples.cpp +++ b/examples/checkboxexamples.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "checkboxexamples.h" #include "components/checkbox.h" #include "exampleview.h" @@ -11,7 +12,10 @@ CheckboxExamples::CheckboxExamples(QWidget *parent) QLayout *layout = widget()->layout(); { - Checkbox *checkbox = new Checkbox; + CheckBox *checkbox = new CheckBox; + checkbox->setText("Do the macarena"); + checkbox->setDisabled(true); + checkbox->setChecked(true); ExampleView *view = new ExampleView; view->setWidget(checkbox); @@ -23,6 +27,12 @@ CheckboxExamples::CheckboxExamples(QWidget *parent) frame->setWidget(view); layout->addWidget(frame); + + + QCheckBox *cb = new QCheckBox; + layout->addWidget(cb); + + connect(cb, SIGNAL(toggled(bool)), checkbox, SLOT(setDisabled(bool))); } } diff --git a/examples/radiobuttonexamples.cpp b/examples/radiobuttonexamples.cpp index 08efc16..9fbb184 100644 --- a/examples/radiobuttonexamples.cpp +++ b/examples/radiobuttonexamples.cpp @@ -22,7 +22,6 @@ RadioButtonExamples::RadioButtonExamples(QWidget *parent) 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"); diff --git a/lib/checkable.cpp b/lib/checkable.cpp new file mode 100644 index 0000000..549b2ee --- /dev/null +++ b/lib/checkable.cpp @@ -0,0 +1,307 @@ +#include "checkable.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "checkable_p.h" +#include "lib/rippleoverlay.h" +#include "lib/style.h" +#include "lib/ripple.h" + +CheckablePrivate::CheckablePrivate(Checkable *q) + : q_ptr(q), + checkedIcon(new CheckableIcon(QIcon("../qt-material-widgets/ic_check_box_black_24px.svg"))), + uncheckedIcon(new CheckableIcon(QIcon("../qt-material-widgets/ic_check_box_outline_blank_black_24px.svg"))), + useThemeColors(true) +{ +} + +CheckablePrivate::~CheckablePrivate() +{ +} + +void CheckablePrivate::init() +{ + Q_Q(Checkable); + + q->setCheckable(true); + + uncheckedIcon->setParent(q); + checkedIcon->setParent(q); + + ripple = new RippleOverlay(q->parentWidget()); + + QFont f(q->font()); + f.setPointSizeF(11); + q->setFont(f); + + // + + machine = new QStateMachine(q); + + uncheckedState = new QState; + checkedState = new QState; + disabledUncheckedState = new QState; + disabledCheckedState = new QState; + + machine->addState(uncheckedState); + machine->addState(checkedState); + machine->addState(disabledUncheckedState); + machine->addState(disabledCheckedState); + + machine->setInitialState(uncheckedState); + + // Transition to checked + + uncheckedTransition = new QSignalTransition(q, SIGNAL(toggled(bool))); + uncheckedTransition->setTargetState(checkedState); + uncheckedState->addTransition(uncheckedTransition); + + // Transition to unchecked + + checkedTransition = new QSignalTransition(q, SIGNAL(toggled(bool))); + checkedTransition->setTargetState(uncheckedState); + checkedState->addTransition(checkedTransition); + + QAbstractTransition *transition; + + // Enabled-Disabled transitions + + transition = new QEventTransition(q, QEvent::EnabledChange); + transition->setTargetState(disabledUncheckedState); + uncheckedState->addTransition(transition); + + transition = new QEventTransition(q, QEvent::EnabledChange); + transition->setTargetState(uncheckedState); + disabledUncheckedState->addTransition(transition); + + transition = new QEventTransition(q, QEvent::EnabledChange); + transition->setTargetState(disabledCheckedState); + checkedState->addTransition(transition); + + transition = new QEventTransition(q, QEvent::EnabledChange); + transition->setTargetState(checkedState); + disabledCheckedState->addTransition(transition); + + transition = new QSignalTransition(q, SIGNAL(toggled(bool))); + transition->setTargetState(disabledCheckedState); + disabledUncheckedState->addTransition(transition); + + transition = new QSignalTransition(q, SIGNAL(toggled(bool))); + transition->setTargetState(disabledUncheckedState); + disabledCheckedState->addTransition(transition); +} + +Checkable::Checkable(QWidget *parent) + : QAbstractButton(parent), + d_ptr(new CheckablePrivate(this)) +{ + d_func()->init(); +} + +Checkable::~Checkable() +{ +} + +void Checkable::setUseThemeColors(bool state) +{ + Q_D(Checkable); + + d->useThemeColors = state; + assignProperties(); + update(); +} + +bool Checkable::useThemeColors() const +{ + Q_D(const Checkable); + + return d->useThemeColors; +} + +void Checkable::setCheckedColor(const QColor &color) +{ + Q_D(Checkable); + + d->checkedColor = color; + setUseThemeColors(false); +} + +QColor Checkable::checkedColor() const +{ + Q_D(const Checkable); + + if (d->useThemeColors || !d->checkedColor.isValid()) { + return Style::instance().themeColor("primary1"); + } else { + return d->checkedColor; + } +} + +void Checkable::setUncheckedColor(const QColor &color) +{ + Q_D(Checkable); + + d->uncheckedColor = color; + setUseThemeColors(false); +} + +QColor Checkable::uncheckedColor() const +{ + Q_D(const Checkable); + + if (d->useThemeColors || !d->uncheckedColor.isValid()) { + return Style::instance().themeColor("text"); + } else { + return d->uncheckedColor; + } +} + +void Checkable::setTextColor(const QColor &color) +{ + Q_D(Checkable); + + d->textColor = color; + setUseThemeColors(false); +} + +QColor Checkable::textColor() const +{ + Q_D(const Checkable); + + if (d->useThemeColors || !d->textColor.isValid()) { + return Style::instance().themeColor("text"); + } else { + return d->textColor; + } +} + +void Checkable::setDisabledColor(const QColor &color) +{ + Q_D(Checkable); + + d->disabledColor = color; + setUseThemeColors(false); +} + +QColor Checkable::disabledColor() const +{ + Q_D(const Checkable); + + 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 Checkable::setCheckedIcon(const QIcon &icon) +{ + Q_D(Checkable); + + d->checkedIcon->setIcon(icon); +} + +QIcon Checkable::checkedIcon() const +{ + Q_D(const Checkable); + + return d->checkedIcon->icon(); +} + +void Checkable::setUncheckedIcon(const QIcon &icon) +{ + Q_D(Checkable); + + d->uncheckedIcon->setIcon(icon); +} + +QIcon Checkable::uncheckedIcon() const +{ + Q_D(const Checkable); + + return d->uncheckedIcon->icon(); +} + +QSize Checkable::sizeHint() const +{ + QString s(text()); + if (s.isEmpty()) + return QSize(32, 32); + + QFontMetrics fm = fontMetrics(); + QSize sz = fm.size(Qt::TextShowMnemonic, s); + + return QSize(sz.width() + 44, 32); +} + +Checkable::Checkable(CheckablePrivate &d, QWidget *parent) + : QAbstractButton(parent), + d_ptr(&d) +{ + d_func()->init(); +} + +bool Checkable::event(QEvent *event) +{ + Q_D(Checkable); + + switch (event->type()) + { + case QEvent::Resize: + case QEvent::Move: + d->ripple->setGeometry(geometry().adjusted(-8, -8, 8, 8)); + d->checkedIcon->setGeometry(rect()); + d->uncheckedIcon->setGeometry(rect()); + break; + case QEvent::ParentChange: + QWidget *widget; + if ((widget = parentWidget())) { + d->ripple->setParent(widget); + } + break; + default: + break; + } + return QAbstractButton::event(event); +} + +void Checkable::mousePressEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + + if (!isEnabled()) + return; + + Q_D(Checkable); + + Ripple *ripple = new Ripple(QPoint(24, 24)); + ripple->setRadiusEndValue(20); + ripple->setColor(isChecked() ? checkedColor() : uncheckedColor()); + d->ripple->addRipple(ripple); + + setChecked(!isChecked()); +} + +void Checkable::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + + QPen pen; + pen.setColor(isEnabled() ? textColor() : disabledColor()); + painter.setPen(pen); + + painter.drawText(44, 21, text()); +} + +void Checkable::assignProperties() +{ +} diff --git a/lib/checkable.h b/lib/checkable.h new file mode 100644 index 0000000..ae585f0 --- /dev/null +++ b/lib/checkable.h @@ -0,0 +1,55 @@ +#ifndef CHECKABLE_H +#define CHECKABLE_H + +#include + +class CheckablePrivate; + +class Checkable : public QAbstractButton +{ + Q_OBJECT + +public: + explicit Checkable(QWidget *parent = 0); + virtual ~Checkable(); + + 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; + + QSize sizeHint() const; + +protected: + Checkable(CheckablePrivate &d, QWidget *parent = 0); + + bool event(QEvent *event) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + + virtual void assignProperties(); + + const QScopedPointer d_ptr; + +private: + Q_DISABLE_COPY(Checkable) + Q_DECLARE_PRIVATE(Checkable) +}; + +#endif // CHECKABLE_H diff --git a/lib/checkable_internal.cpp b/lib/checkable_internal.cpp new file mode 100644 index 0000000..336c0b2 --- /dev/null +++ b/lib/checkable_internal.cpp @@ -0,0 +1,82 @@ +#include "checkable_internal.h" +#include +#include + +CheckableIcon::CheckableIcon(const QIcon &icon, QWidget *parent) + : QWidget(parent), + _effect(new QGraphicsColorizeEffect), + _icon(icon), + _iconSize(24), + _opacity(1.0) +{ + setAttribute(Qt::WA_TransparentForMouseEvents); + + setGraphicsEffect(_effect); +} + +CheckableIcon::~CheckableIcon() +{ +} + +void CheckableIcon::setColor(const QColor &color) +{ + if (_effect->color() == color) + return; + + _effect->setColor(color); + update(); +} + +QColor CheckableIcon::color() const +{ + return _effect->color(); +} + +void CheckableIcon::setIconSize(qreal size) +{ + _iconSize = size; + update(); +} + +qreal CheckableIcon::iconSize() const +{ + return _iconSize; +} + +void CheckableIcon::setOpacity(qreal opacity) +{ + _opacity = opacity; + update(); +} + +qreal CheckableIcon::opacity() const +{ + return _opacity; +} + +void CheckableIcon::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.setRenderHint(QPainter::Antialiasing); + painter.setOpacity(_opacity); + + const qreal p = static_cast((height())-_iconSize)/2; + const qreal z = _iconSize/24; + + QTransform t; + t.translate(p, p); + t.scale(z, z); + painter.setTransform(t); + + _icon.paint(&painter, QRect(0, 0, 24, 24)); +} diff --git a/lib/checkable_internal.h b/lib/checkable_internal.h new file mode 100644 index 0000000..7f17cd3 --- /dev/null +++ b/lib/checkable_internal.h @@ -0,0 +1,45 @@ +#ifndef CHECKABLE_INTERNAL_H +#define CHECKABLE_INTERNAL_H + +#include +#include + +class QGraphicsColorizeEffect; + +class CheckableIcon : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(qreal iconSize READ iconSize WRITE setIconSize) + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) + +public: + CheckableIcon(const QIcon &icon, QWidget *parent = 0); + ~CheckableIcon(); + + 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(qreal size); + qreal iconSize() const; + + void setOpacity(qreal opacity); + qreal opacity() const; + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(CheckableIcon) + + QGraphicsColorizeEffect *const _effect; + QIcon _icon; + qreal _iconSize; + qreal _opacity; +}; + +#endif // CHECKABLE_INTERNAL_H diff --git a/lib/checkable_p.h b/lib/checkable_p.h new file mode 100644 index 0000000..3db4527 --- /dev/null +++ b/lib/checkable_p.h @@ -0,0 +1,42 @@ +#ifndef CHECKABLE_P_H +#define CHECKABLE_P_H + +#include +#include "checkable_internal.h" + +class QStateMachine; +class QState; +class QSignalTransition; +class Checkable; +class RippleOverlay; + +class CheckablePrivate +{ + Q_DISABLE_COPY(CheckablePrivate) + Q_DECLARE_PUBLIC(Checkable) + +public: + CheckablePrivate(Checkable *q); + virtual ~CheckablePrivate(); + + void init(); + + Checkable *const q_ptr; + RippleOverlay *ripple; + CheckableIcon *checkedIcon; + CheckableIcon *uncheckedIcon; + QStateMachine *machine; + QState *uncheckedState; + QState *checkedState; + QState *disabledUncheckedState; + QState *disabledCheckedState; + QSignalTransition *uncheckedTransition; + QSignalTransition *checkedTransition; + QColor checkedColor; + QColor uncheckedColor; + QColor textColor; + QColor disabledColor; + bool useThemeColors; +}; + +#endif // CHECKABLE_P_H diff --git a/qt-material-widgets.pro b/qt-material-widgets.pro index c5a0e1f..bd36b3b 100644 --- a/qt-material-widgets.pro +++ b/qt-material-widgets.pro @@ -60,8 +60,8 @@ SOURCES += main.cpp\ components/badge.cpp \ components/progress.cpp \ components/selectfield.cpp \ - components/radiobutton_internal.cpp \ - components/checkbox_internal.cpp + lib/checkable.cpp \ + lib/checkable_internal.cpp HEADERS += mainwindow.h \ components/appbar.h \ @@ -124,9 +124,10 @@ HEADERS += mainwindow.h \ components/selectfield.h \ components/fab_p.h \ components/radiobutton_p.h \ - components/radiobutton_internal.h \ components/checkbox_p.h \ - components/checkbox_internal.h + lib/checkable.h \ + lib/checkable_p.h \ + lib/checkable_internal.h RESOURCES += \ resources.qrc