From f3a4e06a1b7b8bfa35154badb5bb658dfa2f207f Mon Sep 17 00:00:00 2001 From: lazerpants Date: Fri, 18 Mar 2016 17:36:02 +0300 Subject: [PATCH] add ripple --- flatbutton.cpp | 26 +++++++++++++++++-- flatbutton.h | 9 +++++++ qt-material-widgets.pro | 8 ++++-- ripple.cpp | 32 +++++++++++++++++++++++ ripple.h | 56 ++++++++++++++++++++++++++++++++++++++++ rippleoverlay.cpp | 57 +++++++++++++++++++++++++++++++++++++++++ rippleoverlay.h | 28 ++++++++++++++++++++ 7 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 ripple.cpp create mode 100644 ripple.h create mode 100644 rippleoverlay.cpp create mode 100644 rippleoverlay.h diff --git a/flatbutton.cpp b/flatbutton.cpp index 4be03a8..bfc41a5 100644 --- a/flatbutton.cpp +++ b/flatbutton.cpp @@ -1,15 +1,18 @@ #include #include #include +#include #include "flatbutton.h" FlatButton::FlatButton(QWidget *parent) - : QAbstractButton(parent) + : QAbstractButton(parent), + _overlay(new RippleOverlay(this)) { } FlatButton::FlatButton(const QString &text, QWidget *parent) - : QAbstractButton(parent) + : QAbstractButton(parent), + _overlay(new RippleOverlay(this)) { setText(text); } @@ -18,6 +21,13 @@ FlatButton::~FlatButton() { } +void FlatButton::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + + updateOverlayGeometry(); +} + void FlatButton::paintEvent(QPaintEvent *event) { Q_UNUSED(event) @@ -45,6 +55,18 @@ void FlatButton::paintEvent(QPaintEvent *event) } } +void FlatButton::mousePressEvent(QMouseEvent *event) +{ + _overlay->addRipple(event->pos()); +} + +void FlatButton::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + + emit clicked(); +} + void FlatButton::enterEvent(QEvent *event) { Q_UNUSED(event) diff --git a/flatbutton.h b/flatbutton.h index 658211c..c752e3e 100644 --- a/flatbutton.h +++ b/flatbutton.h @@ -2,6 +2,7 @@ #define FLATBUTTON_H #include +#include "rippleoverlay.h" class FlatButton : public QAbstractButton { @@ -13,9 +14,17 @@ public: ~FlatButton(); protected: + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void enterEvent(QEvent *event) Q_DECL_OVERRIDE; void leaveEvent(QEvent *event) Q_DECL_OVERRIDE; + +private: + inline void updateOverlayGeometry() { _overlay->setGeometry(rect()); } + + RippleOverlay *const _overlay; }; #endif // FLATBUTTON_H diff --git a/qt-material-widgets.pro b/qt-material-widgets.pro index 39fa7dd..7e9895a 100644 --- a/qt-material-widgets.pro +++ b/qt-material-widgets.pro @@ -27,7 +27,9 @@ SOURCES += main.cpp\ tab.cpp \ tabs.cpp \ textfield.cpp \ - table.cpp + table.cpp \ + ripple.cpp \ + rippleoverlay.cpp HEADERS += mainwindow.h \ style.h \ @@ -43,4 +45,6 @@ HEADERS += mainwindow.h \ tab.h \ tabs.h \ textfield.h \ - table.h + table.h \ + ripple.h \ + rippleoverlay.h diff --git a/ripple.cpp b/ripple.cpp new file mode 100644 index 0000000..fe76d2b --- /dev/null +++ b/ripple.cpp @@ -0,0 +1,32 @@ +#include "ripple.h" + +Ripple::Ripple(const QPoint ¢er, QObject *parent) + : QObject(parent), + _radiusAnimation(animate("radius")), + _opacityAnimation(animate("opacity")), + _radius(0), + _opacity(0), + _center(center) +{ + connect(&_group, SIGNAL(finished()), this, SIGNAL(finished())); + + setOpacityStartValue(0.5); + setOpacityEndValue(0); + setRadiusStartValue(0); + setRadiusEndValue(300); +} + +Ripple::~Ripple() +{ +} + +QPropertyAnimation *Ripple::animate(const QByteArray &property) +{ + QPropertyAnimation *animation = new QPropertyAnimation; + animation->setPropertyName(property); + animation->setEasingCurve(QEasingCurve::OutCubic); + animation->setTargetObject(this); + animation->setDuration(700); + _group.addAnimation(animation); + return animation; +} diff --git a/ripple.h b/ripple.h new file mode 100644 index 0000000..2227237 --- /dev/null +++ b/ripple.h @@ -0,0 +1,56 @@ +#ifndef RIPPLE_H +#define RIPPLE_H + +#include +#include +#include +#include + +class Ripple : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal radius WRITE setRadius READ radius) + Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity) + +public: + explicit Ripple(const QPoint ¢er, QObject *parent = 0); + ~Ripple(); + + inline void setRadius(qreal radius) { _radius = radius; emit valueChanged(); } + inline qreal radius() const { return _radius; } + + inline void setOpacity(qreal opacity) { _opacity = opacity; emit valueChanged(); } + inline qreal opacity() const { return _opacity; } + + inline const QPoint ¢er() const { return _center; } + + inline void setDuration(int duration) + { + _radiusAnimation->setDuration(duration); + _opacityAnimation->setDuration(duration); + } + + inline void setOpacityStartValue(qreal value) { _opacityAnimation->setStartValue(value); } + inline void setOpacityEndValue(qreal value) { _opacityAnimation->setEndValue(value); } + inline void setRadiusStartValue(qreal value) { _radiusAnimation->setStartValue(value); } + inline void setRadiusEndValue(qreal value) { _radiusAnimation->setEndValue(value); } + + inline void startAnimation() { _group.start(); } + +signals: + void valueChanged(); + void finished(); + +private: + QPropertyAnimation *animate(const QByteArray &property); + + QParallelAnimationGroup _group; + QPropertyAnimation *const _radiusAnimation; + QPropertyAnimation *const _opacityAnimation; + qreal _radius; + qreal _opacity; + QPoint _center; +}; + +#endif // RIPPLE_H diff --git a/rippleoverlay.cpp b/rippleoverlay.cpp new file mode 100644 index 0000000..189bb82 --- /dev/null +++ b/rippleoverlay.cpp @@ -0,0 +1,57 @@ +#include +#include +#include "rippleoverlay.h" +#include "ripple.h" + +RippleOverlay::RippleOverlay(QWidget *parent) + : QWidget(parent) +{ + setAttribute(Qt::WA_TransparentForMouseEvents); + setAttribute(Qt::WA_NoSystemBackground); +} + +RippleOverlay::~RippleOverlay() +{ +} + +void RippleOverlay::addRipple(const QPoint &position, qreal radius) +{ + Ripple *ripple = new Ripple(position); + ripple->setRadiusEndValue(radius); + ripples.push_back(ripple); + connect(ripple, SIGNAL(valueChanged()), this, SLOT(update())); + connect(ripple, SIGNAL(finished()), this, SLOT(deleteRipple())); + ripple->startAnimation(); +} + +void RippleOverlay::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QBrush brush; + brush.setColor(Qt::black); + brush.setStyle(Qt::SolidPattern); + painter.setBrush(brush); + + QList::const_iterator i; + for (i = ripples.begin(); i != ripples.end(); ++i) + { + const Ripple *ripple = *i; + const qreal radius = ripple->radius(); + const QPointF ¢er = ripple->center(); + painter.setOpacity(ripple->opacity()); + painter.drawEllipse(center, radius, radius); + } +} + +void RippleOverlay::deleteRipple() +{ + if (ripples.isEmpty()) { + qWarning() << "RippleOverlay::deleteRipple was called when no active ripple."; + return; + } + ripples.takeFirst()->deleteLater(); +} diff --git a/rippleoverlay.h b/rippleoverlay.h new file mode 100644 index 0000000..2ac690c --- /dev/null +++ b/rippleoverlay.h @@ -0,0 +1,28 @@ +#ifndef RIPPLEOVERLAY_H +#define RIPPLEOVERLAY_H + +#include + +class Ripple; + +class RippleOverlay : public QWidget +{ + Q_OBJECT + +public: + explicit RippleOverlay(QWidget *parent = 0); + ~RippleOverlay(); + + void addRipple(const QPoint &position, qreal radius = 300); + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + +protected slots: + void deleteRipple(); + +private: + QList ripples; +}; + +#endif // RIPPLEOVERLAY_H