diff --git a/components/components.pro b/components/components.pro index 5bdb6a8..74bf6f4 100644 --- a/components/components.pro +++ b/components/components.pro @@ -5,7 +5,12 @@ SOURCES = \ lib/qtmaterialstyle.cpp \ lib/qtmaterialtheme.cpp \ qtmaterialbadge.cpp \ - lib/qtmaterialoverlaywidget.cpp + lib/qtmaterialoverlaywidget.cpp \ + qtmaterialcheckbox.cpp \ + lib/qtmaterialcheckable_internal.cpp \ + lib/qtmaterialcheckable.cpp \ + lib/qtmaterialripple.cpp \ + lib/qtmaterialrippleoverlay.cpp HEADERS = \ qtmaterialavatar_p.h \ qtmaterialavatar.h \ @@ -15,6 +20,13 @@ HEADERS = \ lib/qtmaterialtheme.h \ qtmaterialbadge_p.h \ qtmaterialbadge.h \ - lib/qtmaterialoverlaywidget.h + lib/qtmaterialoverlaywidget.h \ + qtmaterialcheckbox_p.h \ + qtmaterialcheckbox.h \ + lib/qtmaterialcheckable_internal.h \ + lib/qtmaterialcheckable_p.h \ + lib/qtmaterialripple.h \ + lib/qtmaterialrippleoverlay.h \ + lib/qtmaterialcheckable.h RESOURCES += \ resources.qrc diff --git a/components/lib/qtmaterialcheckable.cpp b/components/lib/qtmaterialcheckable.cpp new file mode 100644 index 0000000..f2a92b1 --- /dev/null +++ b/components/lib/qtmaterialcheckable.cpp @@ -0,0 +1,419 @@ +#include "lib/qtmaterialcheckable.h" +#include "lib/qtmaterialcheckable_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "lib/qtmaterialrippleoverlay.h" +#include "lib/qtmaterialripple.h" +#include "lib/qtmaterialstyle.h" +#include "lib/qtmaterialcheckable_internal.h" + +/*! + * \class QtMaterialCheckablePrivate + * \internal + */ + +QtMaterialCheckablePrivate::QtMaterialCheckablePrivate(QtMaterialCheckable *q) + : q_ptr(q) +{ +} + +QtMaterialCheckablePrivate::~QtMaterialCheckablePrivate() +{ +} + +void QtMaterialCheckablePrivate::init() +{ + Q_Q(QtMaterialCheckable); + + rippleOverlay = new QtMaterialRippleOverlay; + checkedIcon = new QtMaterialCheckableIcon(QIcon(":/icons/icons/toggle/svg/production/ic_check_box_24px.svg"), q); + uncheckedIcon = new QtMaterialCheckableIcon(QIcon(":/icons/icons/toggle/svg/production/ic_check_box_outline_blank_24px.svg"), q); + stateMachine = new QStateMachine(q); + uncheckedState = new QState; + checkedState = new QState; + disabledUncheckedState = new QState; + disabledCheckedState = new QState; + uncheckedTransition = new QSignalTransition(q, SIGNAL(toggled(bool))); + checkedTransition = new QSignalTransition(q, SIGNAL(toggled(bool))); + labelPosition = QtMaterialCheckable::LabelPositionRight; + useThemeColors = true; + + rippleOverlay->setParent(q->parentWidget()); + rippleOverlay->installEventFilter(q); + + q->setCheckable(true); + q->setStyle(&QtMaterialStyle::instance()); + + QFontDatabase db; + QFont font(db.font("Roboto", "Regular", 11)); + q->setFont(font); + + stateMachine->addState(uncheckedState); + stateMachine->addState(checkedState); + stateMachine->addState(disabledUncheckedState); + stateMachine->addState(disabledCheckedState); + stateMachine->setInitialState(uncheckedState); + + // Transition to checked + + uncheckedTransition->setTargetState(checkedState); + uncheckedState->addTransition(uncheckedTransition); + + // Transition to unchecked + + checkedTransition->setTargetState(uncheckedState); + checkedState->addTransition(checkedTransition); + + QAbstractTransition *transition; + + // Transitions enabled <==> disabled + + 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); + + // + + checkedState->assignProperty(checkedIcon, "opacity", 1); + checkedState->assignProperty(uncheckedIcon, "opacity", 0); + + uncheckedState->assignProperty(checkedIcon, "opacity", 0); + uncheckedState->assignProperty(uncheckedIcon, "opacity", 1); + + disabledCheckedState->assignProperty(checkedIcon, "opacity", 1); + disabledCheckedState->assignProperty(uncheckedIcon, "opacity", 0); + + disabledUncheckedState->assignProperty(checkedIcon, "opacity", 0); + disabledUncheckedState->assignProperty(uncheckedIcon, "opacity", 1); + + checkedState->assignProperty(checkedIcon, "color", q->checkedColor()); + checkedState->assignProperty(uncheckedIcon, "color", q->checkedColor()); + + uncheckedState->assignProperty(uncheckedIcon, "color", q->uncheckedColor()); + uncheckedState->assignProperty(uncheckedIcon, "color", q->uncheckedColor()); + + disabledUncheckedState->assignProperty(uncheckedIcon, "color", q->disabledColor()); + disabledCheckedState->assignProperty(checkedIcon, "color", q->disabledColor()); + + stateMachine->start(); + QCoreApplication::processEvents(); +} + +/*! + * \class QtMaterialCheckable + */ + +QtMaterialCheckable::QtMaterialCheckable(QWidget *parent) + : QAbstractButton(parent), + d_ptr(new QtMaterialCheckablePrivate(this)) +{ + d_func()->init(); +} + +QtMaterialCheckable::~QtMaterialCheckable() +{ +} + +void QtMaterialCheckable::setLabelPosition(LabelPosition placement) +{ + Q_D(QtMaterialCheckable); + + d->labelPosition = placement; + update(); +} + +QtMaterialCheckable::LabelPosition QtMaterialCheckable::labelPosition() const +{ + Q_D(const QtMaterialCheckable); + + return d->labelPosition; +} + +void QtMaterialCheckable::setUseThemeColors(bool value) +{ + Q_D(QtMaterialCheckable); + + if (d->useThemeColors == value) { + return; + } + + d->useThemeColors = value; + setupProperties(); +} + +bool QtMaterialCheckable::useThemeColors() const +{ + Q_D(const QtMaterialCheckable); + + return d->useThemeColors; +} + +void QtMaterialCheckable::setCheckedColor(const QColor &color) +{ + Q_D(QtMaterialCheckable); + + d->checkedColor = color; + setUseThemeColors(false); + setupProperties(); +} + +QColor QtMaterialCheckable::checkedColor() const +{ + Q_D(const QtMaterialCheckable); + + if (d->useThemeColors || !d->checkedColor.isValid()) { + return QtMaterialStyle::instance().themeColor("primary1"); + } else { + return d->checkedColor; + } +} + +void QtMaterialCheckable::setUncheckedColor(const QColor &color) +{ + Q_D(QtMaterialCheckable); + + d->uncheckedColor = color; + setUseThemeColors(false); + setupProperties(); +} + +QColor QtMaterialCheckable::uncheckedColor() const +{ + Q_D(const QtMaterialCheckable); + + if (d->useThemeColors || !d->uncheckedColor.isValid()) { + return QtMaterialStyle::instance().themeColor("text"); + } else { + return d->uncheckedColor; + } +} + +void QtMaterialCheckable::setTextColor(const QColor &color) +{ + Q_D(QtMaterialCheckable); + + d->textColor = color; + setUseThemeColors(false); + setupProperties(); +} + +QColor QtMaterialCheckable::textColor() const +{ + Q_D(const QtMaterialCheckable); + + if (d->useThemeColors || !d->textColor.isValid()) { + return QtMaterialStyle::instance().themeColor("text"); + } else { + return d->textColor; + } +} + +void QtMaterialCheckable::setDisabledColor(const QColor &color) +{ + Q_D(QtMaterialCheckable); + + d->disabledColor = color; + setUseThemeColors(false); + setupProperties(); +} + +QColor QtMaterialCheckable::disabledColor() const +{ + Q_D(const QtMaterialCheckable); + + if (d->useThemeColors || !d->disabledColor.isValid()) { + return QtMaterialStyle::instance().themeColor("accent3"); + } else { + return d->disabledColor; + } +} + +void QtMaterialCheckable::setCheckedIcon(const QIcon &icon) +{ + Q_D(QtMaterialCheckable); + + d->checkedIcon->setIcon(icon); + update(); +} + +QIcon QtMaterialCheckable::checkedIcon() const +{ + Q_D(const QtMaterialCheckable); + + return d->checkedIcon->icon(); +} + +void QtMaterialCheckable::setUncheckedIcon(const QIcon &icon) +{ + Q_D(QtMaterialCheckable); + + d->uncheckedIcon->setIcon(icon); + update(); +} + +QIcon QtMaterialCheckable::uncheckedIcon() const +{ + Q_D(const QtMaterialCheckable); + + return d->uncheckedIcon->icon(); +} + +/*! + * \reimp + */ +QSize QtMaterialCheckable::sizeHint() const +{ + if (text().isEmpty()) { + return QSize(40, 40); + } + return QSize(fontMetrics().size(Qt::TextShowMnemonic, text()).width()+52, 40); +} + +QtMaterialCheckable::QtMaterialCheckable(QtMaterialCheckablePrivate &d, QWidget *parent) + : QAbstractButton(parent), + d_ptr(&d) +{ + d_func()->init(); +} + +/*! + * \reimp + */ +bool QtMaterialCheckable::event(QEvent *event) +{ + Q_D(QtMaterialCheckable); + + switch (event->type()) + { + case QEvent::Resize: + case QEvent::Move: + d->checkedIcon->setGeometry(rect()); + d->uncheckedIcon->setGeometry(rect()); + d->rippleOverlay->setGeometry(geometry().adjusted(-8, -8, 8, 8)); + break; + case QEvent::ParentChange: + QWidget *widget; + if ((widget = parentWidget())) { + d->rippleOverlay->setParent(widget); + } + break; + default: + break; + } + return QAbstractButton::event(event); +} + +/*! + * \reimp + */ +bool QtMaterialCheckable::eventFilter(QObject *obj, QEvent *event) +{ + if (QEvent::Resize == event->type()) + { + Q_D(QtMaterialCheckable); + + d->rippleOverlay->setGeometry(geometry().adjusted(-8, -8, 8, 8)); + } + return QAbstractButton::eventFilter(obj, event); +} + +/*! + * \reimp + */ +void QtMaterialCheckable::mousePressEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + + Q_D(QtMaterialCheckable); + + if (!isEnabled()) { + return; + } + + QtMaterialRipple *ripple; + if (QtMaterialCheckable::LabelPositionLeft == d->labelPosition) { + ripple = new QtMaterialRipple(QPoint(width()-14, 28)); + } else { + ripple = new QtMaterialRipple(QPoint(28, 28)); + } + ripple->setRadiusEndValue(22); + ripple->setColor(isChecked() ? checkedColor() : uncheckedColor()); + if (isChecked()) { + ripple->setOpacityStartValue(1); + } + d->rippleOverlay->addRipple(ripple); + + setChecked(!isChecked()); +} + +/*! + * \reimp + */ +void QtMaterialCheckable::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + Q_D(QtMaterialCheckable); + + QPainter painter(this); + + QPen pen; + pen.setColor(isEnabled() ? textColor() : disabledColor()); + painter.setPen(pen); + + if (QtMaterialCheckable::LabelPositionLeft == d->labelPosition) { + painter.drawText(4, 25, text()); + } else { + painter.drawText(48, 25, text()); + } +} + +void QtMaterialCheckable::setupProperties() +{ + Q_D(QtMaterialCheckable); + + d->checkedState->assignProperty(d->checkedIcon, "color", checkedColor()); + d->checkedState->assignProperty(d->uncheckedIcon, "color", checkedColor()); + d->uncheckedState->assignProperty(d->uncheckedIcon, "color", uncheckedColor()); + d->disabledUncheckedState->assignProperty(d->uncheckedIcon, "color", disabledColor()); + d->disabledCheckedState->assignProperty(d->checkedIcon, "color", disabledColor()); + + if (isEnabled()) { + if (isChecked()) { + d->checkedIcon->setColor(checkedColor()); + } else { + d->uncheckedIcon->setColor(uncheckedColor()); + } + } else { + d->checkedIcon->setColor(disabledColor()); + d->uncheckedIcon->setColor(disabledColor()); + } + + update(); +} diff --git a/components/lib/qtmaterialcheckable.h b/components/lib/qtmaterialcheckable.h new file mode 100644 index 0000000..c4cc574 --- /dev/null +++ b/components/lib/qtmaterialcheckable.h @@ -0,0 +1,64 @@ +#ifndef QTMATERIALCHECKABLE_H +#define QTMATERIALCHECKABLE_H + +#include + +class QtMaterialCheckablePrivate; + +class QtMaterialCheckable : public QAbstractButton +{ + Q_OBJECT + +public: + enum LabelPosition { + LabelPositionLeft, + LabelPositionRight, + }; + + explicit QtMaterialCheckable(QWidget *parent = 0); + ~QtMaterialCheckable(); + + void setLabelPosition(LabelPosition placement); + LabelPosition labelPosition() const; + + void setUseThemeColors(bool value); + 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 Q_DECL_OVERRIDE; + +protected: + QtMaterialCheckable(QtMaterialCheckablePrivate &d, QWidget *parent = 0); + + bool event(QEvent *event) Q_DECL_OVERRIDE; + bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + + virtual void setupProperties(); + + const QScopedPointer d_ptr; + +private: + Q_DISABLE_COPY(QtMaterialCheckable) + Q_DECLARE_PRIVATE(QtMaterialCheckable) +}; + +#endif // QTMATERIALCHECKABLE_H diff --git a/components/lib/qtmaterialcheckable_internal.cpp b/components/lib/qtmaterialcheckable_internal.cpp new file mode 100644 index 0000000..4a7eb9a --- /dev/null +++ b/components/lib/qtmaterialcheckable_internal.cpp @@ -0,0 +1,63 @@ +#include "lib/qtmaterialcheckable_internal.h" +#include +#include +#include +#include "lib/qtmaterialcheckable.h" + +/*! + * \class QtMaterialCheckableIcon + * \internal + */ + +QtMaterialCheckableIcon::QtMaterialCheckableIcon(const QIcon &icon, QtMaterialCheckable *parent) + : QWidget(parent), + m_checkable(parent), + m_color(Qt::black), + m_icon(icon), + m_iconSize(24), + m_opacity(1.0) +{ + Q_ASSERT(parent); + + setAttribute(Qt::WA_TransparentForMouseEvents); +} + +QtMaterialCheckableIcon::~QtMaterialCheckableIcon() +{ +} + +QSize QtMaterialCheckableIcon::sizeHint() const +{ + return QSize(m_iconSize, m_iconSize); +} + +void QtMaterialCheckableIcon::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setOpacity(m_opacity); + + QPixmap pixmap = icon().pixmap(24, 24); + + if (!pixmap.isNull()) + { + const qreal p = static_cast((height())-m_iconSize)/2; + const qreal z = m_iconSize/24; + + QTransform t; + if (QtMaterialCheckable::LabelPositionLeft == m_checkable->labelPosition()) { + t.translate(p+width()-42, p); + } else { + t.translate(p, p); + } + t.scale(z, z); + painter.setTransform(t); + + QPainter icon(&pixmap); + icon.setCompositionMode(QPainter::CompositionMode_SourceIn); + icon.fillRect(pixmap.rect(), color()); + painter.drawPixmap(0, 0, pixmap); + } +} diff --git a/components/lib/qtmaterialcheckable_internal.h b/components/lib/qtmaterialcheckable_internal.h new file mode 100644 index 0000000..3fb3d94 --- /dev/null +++ b/components/lib/qtmaterialcheckable_internal.h @@ -0,0 +1,93 @@ +#ifndef QTMATERIALCHECKABLE_INTERNAL_H +#define QTMATERIALCHECKABLE_INTERNAL_H + +#include +#include +#include + +class QtMaterialCheckable; + +class QtMaterialCheckableIcon : 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: + QtMaterialCheckableIcon(const QIcon &icon, QtMaterialCheckable *parent); + ~QtMaterialCheckableIcon(); + + QSize sizeHint() const Q_DECL_OVERRIDE; + + inline void setIcon(const QIcon &icon); + inline QIcon icon() const; + + inline void setColor(const QColor &color); + inline QColor color() const; + + inline void setIconSize(qreal size); + inline qreal iconSize() const; + + inline void setOpacity(qreal opacity); + inline qreal opacity() const; + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(QtMaterialCheckableIcon) + + QtMaterialCheckable *const m_checkable; + QColor m_color; + QIcon m_icon; + qreal m_iconSize; + qreal m_opacity; +}; + +inline void QtMaterialCheckableIcon::setIcon(const QIcon &icon) +{ + m_icon = icon; + update(); +} + +inline QIcon QtMaterialCheckableIcon::icon() const +{ + return m_icon; +} + +inline void QtMaterialCheckableIcon::setColor(const QColor &color) +{ + m_color = color; + update(); +} + +inline QColor QtMaterialCheckableIcon::color() const +{ + return m_color; +} + +inline void QtMaterialCheckableIcon::setIconSize(qreal size) +{ + m_iconSize = size; + update(); +} + +inline qreal QtMaterialCheckableIcon::iconSize() const +{ + return m_iconSize; +} + +inline void QtMaterialCheckableIcon::setOpacity(qreal opacity) +{ + m_opacity = opacity; + update(); +} + +inline qreal QtMaterialCheckableIcon::opacity() const +{ + return m_opacity; +} + +#endif // QTMATERIALCHECKABLE_INTERNAL_H diff --git a/components/lib/qtmaterialcheckable_p.h b/components/lib/qtmaterialcheckable_p.h new file mode 100644 index 0000000..e678df7 --- /dev/null +++ b/components/lib/qtmaterialcheckable_p.h @@ -0,0 +1,43 @@ +#ifndef QTMATERIALCHECKABLE_P_H +#define QTMATERIALCHECKABLE_P_H + +#include +#include "lib/qtmaterialcheckable.h" + +class QStateMachine; +class QState; +class QSignalTransition; +class QtMaterialRippleOverlay; +class QtMaterialCheckableIcon; + +class QtMaterialCheckablePrivate +{ + Q_DISABLE_COPY(QtMaterialCheckablePrivate) + Q_DECLARE_PUBLIC(QtMaterialCheckable) + +public: + QtMaterialCheckablePrivate(QtMaterialCheckable *q); + virtual ~QtMaterialCheckablePrivate(); + + void init(); + + QtMaterialCheckable *const q_ptr; + QtMaterialRippleOverlay *rippleOverlay; + QtMaterialCheckableIcon *checkedIcon; + QtMaterialCheckableIcon *uncheckedIcon; + QStateMachine *stateMachine; + QState *uncheckedState; + QState *checkedState; + QState *disabledUncheckedState; + QState *disabledCheckedState; + QSignalTransition *uncheckedTransition; + QSignalTransition *checkedTransition; + QtMaterialCheckable::LabelPosition labelPosition; + QColor checkedColor; + QColor uncheckedColor; + QColor textColor; + QColor disabledColor; + bool useThemeColors; +}; + +#endif // QTMATERIALCHECKABLE_P_H diff --git a/components/lib/qtmaterialripple.cpp b/components/lib/qtmaterialripple.cpp new file mode 100644 index 0000000..82125cb --- /dev/null +++ b/components/lib/qtmaterialripple.cpp @@ -0,0 +1,119 @@ +#include "qtmaterialripple.h" +#include "lib/qtmaterialrippleoverlay.h" + +/*! + * \class QtMaterialRipple + * \internal + */ + +QtMaterialRipple::QtMaterialRipple(const QPoint ¢er, QObject *parent) + : QParallelAnimationGroup(parent), + m_overlay(0), + m_radiusAnimation(animate("radius")), + m_opacityAnimation(animate("opacity")), + m_radius(0), + m_opacity(0), + m_center(center) +{ + init(); +} + +QtMaterialRipple::QtMaterialRipple(const QPoint ¢er, + QtMaterialRippleOverlay *overlay, + QObject *parent) + : QParallelAnimationGroup(parent), + m_overlay(overlay), + m_radiusAnimation(animate("radius")), + m_opacityAnimation(animate("opacity")), + m_radius(0), + m_opacity(0), + m_center(center) +{ + init(); +} + +QtMaterialRipple::~QtMaterialRipple() +{ +} + +void QtMaterialRipple::setRadius(qreal radius) +{ + Q_ASSERT(m_overlay); + + if (m_radius == radius) { + return; + } + m_radius = radius; + m_overlay->update(); +} + +void QtMaterialRipple::setOpacity(qreal opacity) +{ + Q_ASSERT(m_overlay); + + if (m_opacity == opacity) { + return; + } + m_opacity = opacity; + m_overlay->update(); +} + +void QtMaterialRipple::setColor(const QColor &color) +{ + if (m_brush.color() == color) { + return; + } + m_brush.setColor(color); + + if (m_overlay) { + m_overlay->update(); + } +} + +void QtMaterialRipple::setBrush(const QBrush &brush) +{ + m_brush = brush; + + if (m_overlay) { + m_overlay->update(); + } +} + +void QtMaterialRipple::destroy() +{ + Q_ASSERT(m_overlay); + + m_overlay->removeRipple(this); +} + +/*! + * \internal + */ +QPropertyAnimation *QtMaterialRipple::animate(const QByteArray &property, + const QEasingCurve &easing, + int duration) +{ + QPropertyAnimation *animation = new QPropertyAnimation; + animation->setTargetObject(this); + animation->setPropertyName(property); + animation->setEasingCurve(easing); + animation->setDuration(duration); + addAnimation(animation); + return animation; +} + +/*! + * \internal + */ +void QtMaterialRipple::init() +{ + setOpacityStartValue(0.5); + setOpacityEndValue(0); + setRadiusStartValue(0); + setRadiusEndValue(300); + + m_brush.setColor(Qt::black); + m_brush.setStyle(Qt::SolidPattern); + + connect(this, SIGNAL(finished()), this, SLOT(destroy())); +} diff --git a/components/lib/qtmaterialripple.h b/components/lib/qtmaterialripple.h new file mode 100644 index 0000000..d263c76 --- /dev/null +++ b/components/lib/qtmaterialripple.h @@ -0,0 +1,136 @@ +#ifndef QTMATERIALRIPPLE_H +#define QTMATERIALRIPPLE_H + +#include +#include +#include +#include +#include + +class QtMaterialRippleOverlay; + +class QtMaterialRipple : public QParallelAnimationGroup +{ + Q_OBJECT + + Q_PROPERTY(qreal radius WRITE setRadius READ radius) + Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity) + +public: + explicit QtMaterialRipple(const QPoint ¢er, QObject *parent = 0); + QtMaterialRipple(const QPoint ¢er, QtMaterialRippleOverlay *overlay, QObject *parent = 0); + ~QtMaterialRipple(); + + inline void setOverlay(QtMaterialRippleOverlay *overlay); + + void setRadius(qreal radius); + inline qreal radius() const; + + void setOpacity(qreal opacity); + inline qreal opacity() const; + + void setColor(const QColor &color); + inline QColor color() const; + + void setBrush(const QBrush &brush); + inline QBrush brush() const; + + inline QPoint center() const; + + inline QPropertyAnimation *radiusAnimation() const; + inline QPropertyAnimation *opacityAnimation() const; + + inline void setOpacityStartValue(qreal value); + inline void setOpacityEndValue(qreal value); + inline void setRadiusStartValue(qreal value); + inline void setRadiusEndValue(qreal value); + inline void setDuration(int msecs); + +protected slots: + void destroy(); + +private: + Q_DISABLE_COPY(QtMaterialRipple) + + QPropertyAnimation *animate(const QByteArray &property, + const QEasingCurve &easing = QEasingCurve::OutQuad, + int duration = 800); + + void init(); + + QtMaterialRippleOverlay *m_overlay; + QPropertyAnimation *const m_radiusAnimation; + QPropertyAnimation *const m_opacityAnimation; + qreal m_radius; + qreal m_opacity; + QPoint m_center; + QBrush m_brush; +}; + +inline void QtMaterialRipple::setOverlay(QtMaterialRippleOverlay *overlay) +{ + m_overlay = overlay; +} + +inline qreal QtMaterialRipple::radius() const +{ + return m_radius; +} + +inline qreal QtMaterialRipple::opacity() const +{ + return m_opacity; +} + +inline QColor QtMaterialRipple::color() const +{ + return m_brush.color(); +} + +inline QBrush QtMaterialRipple::brush() const +{ + return m_brush; +} + +inline QPoint QtMaterialRipple::center() const +{ + return m_center; +} + +inline QPropertyAnimation *QtMaterialRipple::radiusAnimation() const +{ + return m_radiusAnimation; +} + +inline QPropertyAnimation *QtMaterialRipple::opacityAnimation() const +{ + return m_opacityAnimation; +} + +inline void QtMaterialRipple::setOpacityStartValue(qreal value) +{ + m_opacityAnimation->setStartValue(value); +} + +inline void QtMaterialRipple::setOpacityEndValue(qreal value) +{ + m_opacityAnimation->setEndValue(value); +} + +inline void QtMaterialRipple::setRadiusStartValue(qreal value) +{ + m_radiusAnimation->setStartValue(value); +} + +inline void QtMaterialRipple::setRadiusEndValue(qreal value) +{ + m_radiusAnimation->setEndValue(value); +} + +inline void QtMaterialRipple::setDuration(int msecs) +{ + m_radiusAnimation->setDuration(msecs); + m_opacityAnimation->setDuration(msecs); +} + +#endif // QTMATERIALRIPPLE_H diff --git a/components/lib/qtmaterialrippleoverlay.cpp b/components/lib/qtmaterialrippleoverlay.cpp new file mode 100644 index 0000000..b13ecbf --- /dev/null +++ b/components/lib/qtmaterialrippleoverlay.cpp @@ -0,0 +1,71 @@ +#include "lib/qtmaterialrippleoverlay.h" +#include +#include "lib/qtmaterialripple.h" + +/*! + * \class QtMaterialRippleOverlay + * \internal + */ + +QtMaterialRippleOverlay::QtMaterialRippleOverlay(QWidget *parent) + : QtMaterialOverlayWidget(parent), + m_useClip(false) +{ + setAttribute(Qt::WA_TransparentForMouseEvents); + setAttribute(Qt::WA_NoSystemBackground); +} + +QtMaterialRippleOverlay::~QtMaterialRippleOverlay() +{ +} + +void QtMaterialRippleOverlay::addRipple(QtMaterialRipple *ripple) +{ + ripple->setOverlay(this); + m_ripples.push_back(ripple); + ripple->start(); +} + +void QtMaterialRippleOverlay::addRipple(const QPoint &position, qreal radius) +{ + QtMaterialRipple *ripple = new QtMaterialRipple(position); + ripple->setRadiusEndValue(radius); + addRipple(ripple); +} + +void QtMaterialRippleOverlay::removeRipple(QtMaterialRipple *ripple) +{ + if (m_ripples.removeOne(ripple)) { + delete ripple; + } +} + +/*! + * \reimp + */ +void QtMaterialRippleOverlay::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + + if (m_useClip) { + painter.setClipPath(m_clipPath); + } + + QList::const_iterator i; + for (i = m_ripples.begin(); i != m_ripples.end(); ++i) { + paintRipple(&painter, *i); + } +} + +void QtMaterialRippleOverlay::paintRipple(QPainter *painter, QtMaterialRipple *ripple) +{ + const qreal radius = ripple->radius(); + const QPointF center = ripple->center(); + painter->setOpacity(ripple->opacity()); + painter->setBrush(ripple->brush()); + painter->drawEllipse(center, radius, radius); +} diff --git a/components/lib/qtmaterialrippleoverlay.h b/components/lib/qtmaterialrippleoverlay.h new file mode 100644 index 0000000..436c2b9 --- /dev/null +++ b/components/lib/qtmaterialrippleoverlay.h @@ -0,0 +1,64 @@ +#ifndef QTMATERIALRIPPLEOVERLAY_H +#define QTMATERIALRIPPLEOVERLAY_H + +#include +#include "lib/qtmaterialoverlaywidget.h" + +class QtMaterialRipple; + +class QtMaterialRippleOverlay : public QtMaterialOverlayWidget +{ + Q_OBJECT + +public: + explicit QtMaterialRippleOverlay(QWidget *parent = 0); + ~QtMaterialRippleOverlay(); + + void addRipple(QtMaterialRipple *ripple); + void addRipple(const QPoint &position, qreal radius = 300); + + void removeRipple(QtMaterialRipple *ripple); + + inline void setClipping(bool enable); + inline bool hasClipping() const; + + inline void setClipPath(const QPainterPath &path); + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + + inline QList ripples() const; + +private: + Q_DISABLE_COPY(QtMaterialRippleOverlay) + + void paintRipple(QPainter *painter, QtMaterialRipple *ripple); + + QList m_ripples; + QPainterPath m_clipPath; + bool m_useClip; +}; + +inline void QtMaterialRippleOverlay::setClipping(bool enable) +{ + m_useClip = enable; + update(); +} + +inline bool QtMaterialRippleOverlay::hasClipping() const +{ + return m_useClip; +} + +inline void QtMaterialRippleOverlay::setClipPath(const QPainterPath &path) +{ + m_clipPath = path; + update(); +} + +inline QList QtMaterialRippleOverlay::ripples() const +{ + return m_ripples; +} + +#endif // QTMATERIALRIPPLEOVERLAY_H diff --git a/components/qtmaterialcheckbox.cpp b/components/qtmaterialcheckbox.cpp new file mode 100644 index 0000000..b693a90 --- /dev/null +++ b/components/qtmaterialcheckbox.cpp @@ -0,0 +1,85 @@ +#include "qtmaterialcheckbox.h" +#include "qtmaterialcheckbox_p.h" +#include +#include +#include +#include "lib/qtmaterialcheckable_internal.h" + +/*! + * \class QtMaterialCheckBoxPrivate + * \internal + */ + +/*! + * \internal + */ +QtMaterialCheckBoxPrivate::QtMaterialCheckBoxPrivate(QtMaterialCheckBox *q) + : QtMaterialCheckablePrivate(q) +{ +} + +/*! + * \internal + */ +QtMaterialCheckBoxPrivate::~QtMaterialCheckBoxPrivate() +{ +} + +/*! + * \internal + */ +void QtMaterialCheckBoxPrivate::init() +{ + Q_Q(QtMaterialCheckBox); + + checkedState->assignProperty(checkedIcon, "iconSize", 24); + uncheckedState->assignProperty(checkedIcon, "iconSize", 0); + + QPropertyAnimation *animation; + + animation = new QPropertyAnimation(checkedIcon, "iconSize", q); + animation->setDuration(300); + uncheckedTransition->addAnimation(animation); + + animation = new QPropertyAnimation(checkedIcon, "iconSize", q); + animation->setDuration(1300); + checkedTransition->addAnimation(animation); + + animation = new QPropertyAnimation(checkedIcon, "opacity", q); + animation->setDuration(440); + checkedTransition->addAnimation(animation); + + animation = new QPropertyAnimation(checkedIcon, "opacity", q); + animation->setDuration(440); + uncheckedTransition->addAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "opacity", q); + animation->setDuration(440); + checkedTransition->addAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "opacity", q); + animation->setDuration(440); + uncheckedTransition->addAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "color", q); + animation->setDuration(440); + checkedTransition->addAnimation(animation); + + animation = new QPropertyAnimation(uncheckedIcon, "color", q); + animation->setDuration(440); + uncheckedTransition->addAnimation(animation); +} + +/*! + * \class QtMaterialCheckBox + */ + +QtMaterialCheckBox::QtMaterialCheckBox(QWidget *parent) + : QtMaterialCheckable(*new QtMaterialCheckBoxPrivate(this), parent) +{ + d_func()->init(); +} + +QtMaterialCheckBox::~QtMaterialCheckBox() +{ +} diff --git a/components/qtmaterialcheckbox.h b/components/qtmaterialcheckbox.h new file mode 100644 index 0000000..5aaa8f4 --- /dev/null +++ b/components/qtmaterialcheckbox.h @@ -0,0 +1,21 @@ +#ifndef QTMATERIALCHECKBOX_H +#define QTMATERIALCHECKBOX_H + +#include "lib/qtmaterialcheckable.h" + +class QtMaterialCheckBoxPrivate; + +class QtMaterialCheckBox : public QtMaterialCheckable +{ + Q_OBJECT + +public: + explicit QtMaterialCheckBox(QWidget *parent = 0); + ~QtMaterialCheckBox(); + +private: + Q_DISABLE_COPY(QtMaterialCheckBox) + Q_DECLARE_PRIVATE(QtMaterialCheckBox) +}; + +#endif // QTMATERIALCHECKBOX_H diff --git a/components/qtmaterialcheckbox_p.h b/components/qtmaterialcheckbox_p.h new file mode 100644 index 0000000..daa62d7 --- /dev/null +++ b/components/qtmaterialcheckbox_p.h @@ -0,0 +1,20 @@ +#ifndef QTMATERIALCHECKBOX_P_H +#define QTMATERIALCHECKBOX_P_H + +#include "lib/qtmaterialcheckable_p.h" + +class QtMaterialCheckBox; + +class QtMaterialCheckBoxPrivate : public QtMaterialCheckablePrivate +{ + Q_DISABLE_COPY(QtMaterialCheckBoxPrivate) + Q_DECLARE_PUBLIC(QtMaterialCheckBox) + +public: + QtMaterialCheckBoxPrivate(QtMaterialCheckBox *q); + ~QtMaterialCheckBoxPrivate(); + + void init(); +}; + +#endif // QTMATERIALCHECKBOX_P_H diff --git a/components/resources.qrc b/components/resources.qrc index bf99339..440683a 100644 --- a/components/resources.qrc +++ b/components/resources.qrc @@ -9,5 +9,13 @@ icons/communication/svg/production/ic_message_24px.svg + icons/toggle/svg/production/ic_check_box_24px.svg + icons/toggle/svg/production/ic_check_box_outline_blank_24px.svg + icons/toggle/svg/production/ic_indeterminate_check_box_24px.svg + icons/toggle/svg/production/ic_radio_button_checked_24px.svg + icons/toggle/svg/production/ic_radio_button_unchecked_24px.svg + icons/toggle/svg/production/ic_star_24px.svg + icons/toggle/svg/production/ic_star_border_24px.svg + icons/toggle/svg/production/ic_star_half_24px.svg diff --git a/examples/checkboxsettingseditor.cpp b/examples/checkboxsettingseditor.cpp new file mode 100644 index 0000000..b7e5d9b --- /dev/null +++ b/examples/checkboxsettingseditor.cpp @@ -0,0 +1,112 @@ +#include "checkboxsettingseditor.h" +#include +#include +#include "qtmaterialcheckbox.h" + +CheckBoxSettingsEditor::CheckBoxSettingsEditor(QWidget *parent) + : QWidget(parent), + ui(new Ui::CheckBoxSettingsForm), + m_checkBox(new QtMaterialCheckBox) +{ + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + + QWidget *widget = new QWidget; + layout->addWidget(widget); + + QWidget *canvas = new QWidget; + canvas->setStyleSheet("QWidget { background: white; }"); + layout->addWidget(canvas); + + ui->setupUi(widget); + layout->setContentsMargins(20, 20, 20, 20); + + m_checkBox->setText("Palak paneer"); + m_checkBox->setChecked(true); + + layout = new QVBoxLayout; + canvas->setLayout(layout); + layout->addWidget(m_checkBox); + layout->setAlignment(m_checkBox, Qt::AlignCenter); + + setupForm(); + + connect(ui->disabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateWidget())); + connect(ui->labelPositionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateWidget())); + connect(ui->labelTextLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateWidget())); + connect(ui->useThemeColorsCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateWidget())); + connect(ui->checkedCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateWidget())); + connect(ui->textColorToolButton, SIGNAL(pressed()), this, SLOT(selectColor())); + connect(ui->disabledColorToolButton, SIGNAL(pressed()), this, SLOT(selectColor())); + connect(ui->checkedColorToolButton, SIGNAL(pressed()), this, SLOT(selectColor())); + connect(ui->uncheckedColorToolButton, SIGNAL(pressed()), this, SLOT(selectColor())); + connect(m_checkBox, SIGNAL(toggled(bool)), this, SLOT(setupForm())); +} + +CheckBoxSettingsEditor::~CheckBoxSettingsEditor() +{ + delete ui; +} + +void CheckBoxSettingsEditor::setupForm() +{ + switch (m_checkBox->labelPosition()) + { + case QtMaterialCheckable::LabelPositionLeft: + ui->labelPositionComboBox->setCurrentIndex(0); + break; + case QtMaterialCheckable::LabelPositionRight: + ui->labelPositionComboBox->setCurrentIndex(1); + break; + default: + break; + } + + ui->disabledCheckBox->setChecked(!m_checkBox->isEnabled()); + ui->labelTextLineEdit->setText(m_checkBox->text()); + ui->useThemeColorsCheckBox->setChecked(m_checkBox->useThemeColors()); + ui->checkedCheckBox->setChecked(m_checkBox->isChecked()); +} + +void CheckBoxSettingsEditor::updateWidget() +{ + switch (ui->labelPositionComboBox->currentIndex()) + { + case 0: + m_checkBox->setLabelPosition(QtMaterialCheckable::LabelPositionLeft); + break; + case 1: + m_checkBox->setLabelPosition(QtMaterialCheckable::LabelPositionRight); + break; + default: + break; + } + + m_checkBox->setDisabled(ui->disabledCheckBox->isChecked()); + m_checkBox->setText(ui->labelTextLineEdit->text()); + m_checkBox->setUseThemeColors(ui->useThemeColorsCheckBox->isChecked()); + m_checkBox->setChecked(ui->checkedCheckBox->isChecked()); +} + +void CheckBoxSettingsEditor::selectColor() +{ + QColorDialog dialog; + if (dialog.exec()) { + QColor color = dialog.selectedColor(); + QString senderName = sender()->objectName(); + if ("textColorToolButton" == senderName) { + m_checkBox->setTextColor(color); + ui->textColorLineEdit->setText(color.name(QColor::HexRgb)); + } else if ("disabledColorToolButton" == senderName) { + m_checkBox->setDisabledColor(color); + ui->disabledColorLineEdit->setText(color.name(QColor::HexRgb)); + } else if ("checkedColorToolButton" == senderName) { + m_checkBox->setCheckedColor(color); + ui->checkedColorLineEdit->setText(color.name(QColor::HexRgb)); + } else if ("uncheckedColorToolButton" == senderName) { + m_checkBox->setUncheckedColor(color); + ui->uncheckedColorLineEdit->setText(color.name(QColor::HexRgb)); + } + } + setupForm(); +} diff --git a/examples/checkboxsettingseditor.h b/examples/checkboxsettingseditor.h new file mode 100644 index 0000000..4456367 --- /dev/null +++ b/examples/checkboxsettingseditor.h @@ -0,0 +1,27 @@ +#ifndef CHECKBOXSETTINGSEDITOR_H +#define CHECKBOXSETTINGSEDITOR_H + +#include +#include "ui_checkboxsettingsform.h" + +class QtMaterialCheckable; + +class CheckBoxSettingsEditor : public QWidget +{ + Q_OBJECT + +public: + explicit CheckBoxSettingsEditor(QWidget *parent = 0); + ~CheckBoxSettingsEditor(); + +protected slots: + void setupForm(); + void updateWidget(); + void selectColor(); + +private: + Ui::CheckBoxSettingsForm *const ui; + QtMaterialCheckable *const m_checkBox; +}; + +#endif // CHECKBOXSETTINGSEDITOR_H diff --git a/examples/checkboxsettingsform.ui b/examples/checkboxsettingsform.ui new file mode 100644 index 0000000..842f9d0 --- /dev/null +++ b/examples/checkboxsettingsform.ui @@ -0,0 +1,176 @@ + + + CheckBoxSettingsForm + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + 0 + 0 + 301 + 281 + + + + + + + Disabled + + + + + + + + + + Label position + + + + + + + + Left + + + + + Right + + + + + + + + Label text + + + + + + + + + + Checked + + + + + + + + + + Use theme colors + + + + + + + + + + Checked color + + + + + + + + + + + + ... + + + + + + + + + Unchecked color + + + + + + + + + + + + ... + + + + + + + + + Text color + + + + + + + Disabled color + + + + + + + + + + + + ... + + + + + + + + + + + + + + ... + + + + + + + + + + + diff --git a/examples/examples.pro b/examples/examples.pro index 76b1058..8b9e262 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -3,10 +3,12 @@ TEMPLATE = app SOURCES = mainwindow.cpp \ main.cpp \ avatarsettingseditor.cpp \ - badgesettingseditor.cpp + badgesettingseditor.cpp \ + checkboxsettingseditor.cpp HEADERS = mainwindow.h \ avatarsettingseditor.h \ - badgesettingseditor.h + badgesettingseditor.h \ + checkboxsettingseditor.h LIBS += ../components/libcomponents.a INCLUDEPATH += ../components/ TARGET = ../examples-exe @@ -16,4 +18,5 @@ RESOURCES += \ FORMS += \ avatarsettingsform.ui \ - badgesettingsform.ui + badgesettingsform.ui \ + checkboxsettingsform.ui diff --git a/examples/mainwindow.cpp b/examples/mainwindow.cpp index 64ba5da..1bb7a35 100644 --- a/examples/mainwindow.cpp +++ b/examples/mainwindow.cpp @@ -4,6 +4,7 @@ #include #include "avatarsettingseditor.h" #include "badgesettingseditor.h" +#include "checkboxsettingseditor.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) @@ -25,12 +26,15 @@ MainWindow::MainWindow(QWidget *parent) AvatarSettingsEditor *avatar = new AvatarSettingsEditor; BadgeSettingsEditor *badge = new BadgeSettingsEditor; + CheckBoxSettingsEditor *checkbox = new CheckBoxSettingsEditor; stack->addWidget(avatar); stack->addWidget(badge); + stack->addWidget(checkbox); list->addItem("Avatar"); list->addItem("Badge"); + list->addItem("Checkbox"); list->setCurrentRow(0);