From 03f2571f34687927b3cd64f0b44c5c7429ef6377 Mon Sep 17 00:00:00 2001 From: laserpants Date: Fri, 24 Jun 2016 07:50:24 +0300 Subject: [PATCH] implement Drawer --- components/drawer.cpp | 112 ++++++++++++++++++++++++++++++++- components/drawer.h | 27 +++++++- components/drawer_internal.cpp | 61 ++++++++++++++++++ components/drawer_internal.h | 32 ++++++++++ components/drawer_p.h | 8 ++- lib/overlaywidget.cpp | 57 +++++++++++++++++ lib/overlaywidget.h | 20 ++++++ lib/testrippleoverlay.cpp | 86 +++++++++++++++++++++++++ lib/testrippleoverlay.h | 32 ++++++++++ mainwindow.cpp | 33 +++++++++- qt-material-widgets.pro | 10 ++- 11 files changed, 468 insertions(+), 10 deletions(-) diff --git a/components/drawer.cpp b/components/drawer.cpp index c3b50c2..4936288 100644 --- a/components/drawer.cpp +++ b/components/drawer.cpp @@ -1,16 +1,122 @@ #include "drawer.h" #include "drawer_p.h" +#include +#include +#include +#include +#include +#include +#include "drawer_internal.h" -DrawerPrivate(Drawer *q) - : q_ptr(q) +DrawerPrivate::DrawerPrivate(Drawer *q) + : q_ptr(q), + stateMachine(new DrawerStateMachine(q)), + window(new QWidget), + width(250) { + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(window); + + q->setLayout(layout); + q->setFixedWidth(width+16); + + stateMachine->start(); + + QCoreApplication::processEvents(); } Drawer::Drawer(QWidget *parent) - : QWidget(parent) + : OverlayWidget(parent), + d_ptr(new DrawerPrivate(this)) { } Drawer::~Drawer() { } + +void Drawer::setDrawerWidth(int width) +{ + Q_D(Drawer); + + d->width = width; + d->stateMachine->assignProperties(); + setFixedWidth(width+16); +} + +int Drawer::drawerWidth() const +{ + Q_D(const Drawer); + + return d->width; +} + +void Drawer::setDrawerLayout(QLayout *layout) +{ + Q_D(Drawer); + + d->window->setLayout(layout); +} + +QLayout *Drawer::drawerLayout() const +{ + Q_D(const Drawer); + + return d->window->layout(); +} + +void Drawer::openDrawer() +{ + Q_D(Drawer); + + emit d->stateMachine->enterOpenedState(); +} + +void Drawer::closeDrawer() +{ + Q_D(Drawer); + + emit d->stateMachine->enterClosedState(); +} + +bool Drawer::eventFilter(QObject *obj, QEvent *event) +{ + const QEvent::Type type = event->type(); + + if (QEvent::Move == type || QEvent::Resize == type) { + if (layout() && 16 != layout()->contentsMargins().right()) { + layout()->setContentsMargins(0, 0, 16, 0); + } + } + return OverlayWidget::eventFilter(obj, event); +} + +void Drawer::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + + QBrush brush; + brush.setStyle(Qt::SolidPattern); + brush.setColor(Qt::white); + painter.setBrush(brush); + painter.setPen(Qt::NoPen); + + painter.drawRect(rect().adjusted(0, 0, -16, 0)); + + QLinearGradient gradient(QPointF(width()-16, 0), QPointF(width(), 0)); + gradient.setColorAt(0, QColor(0, 0, 0, 80)); + gradient.setColorAt(0.5, QColor(0, 0, 0, 20)); + gradient.setColorAt(1, QColor(0, 0, 0, 0)); + painter.setBrush(QBrush(gradient)); + + painter.drawRect(width()-16, 0, 16, height()); +} + +QRect Drawer::overlayGeometry() const +{ + Q_D(const Drawer); + + return OverlayWidget::overlayGeometry().translated(d->stateMachine->offset(), 0); +} diff --git a/components/drawer.h b/components/drawer.h index 2ac289d..1d4ae2d 100644 --- a/components/drawer.h +++ b/components/drawer.h @@ -1,9 +1,12 @@ #ifndef DRAWER_H #define DRAWER_H -#include +#include "lib/overlaywidget.h" -class Drawer : public QWidget +class DrawerPrivate; +class DrawerStateMachine; + +class Drawer : public OverlayWidget { Q_OBJECT @@ -11,11 +14,29 @@ public: explicit Drawer(QWidget *parent = 0); ~Drawer(); - Drawer *const q_ptr; + void setDrawerWidth(int width); + int drawerWidth() const; + + void setDrawerLayout(QLayout *layout); + QLayout *drawerLayout() const; + +protected slots: + void openDrawer(); + void closeDrawer(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + + QRect overlayGeometry() const; + + const QScopedPointer d_ptr; private: Q_DISABLE_COPY(Drawer) Q_DECLARE_PRIVATE(Drawer) + +// friend class DrawerStateMachine; }; #endif // DRAWER_H diff --git a/components/drawer_internal.cpp b/components/drawer_internal.cpp index e69de29..5ca7ee2 100644 --- a/components/drawer_internal.cpp +++ b/components/drawer_internal.cpp @@ -0,0 +1,61 @@ +#include "drawer_internal.h" +#include +#include +#include +#include "drawer.h" + +DrawerStateMachine::DrawerStateMachine(Drawer *drawer) + : QStateMachine(drawer), + m_drawer(drawer), + m_openState(new QState), + m_closedState(new QState), + m_offset(0) +{ + addState(m_openState); + addState(m_closedState); + + setInitialState(m_closedState); + + QSignalTransition *transition; + QPropertyAnimation *animation; + + transition = new QSignalTransition(this, SIGNAL(enterOpenedState())); + transition->setTargetState(m_openState); + m_closedState->addTransition(transition); + + animation = new QPropertyAnimation(this, "offset", this); + animation->setDuration(220); + animation->setEasingCurve(QEasingCurve::OutCirc); + transition->addAnimation(animation); + + transition = new QSignalTransition(this, SIGNAL(enterClosedState())); + transition->setTargetState(m_closedState); + m_openState->addTransition(transition); + + animation = new QPropertyAnimation(this, "offset", this); + animation->setDuration(220); + animation->setEasingCurve(QEasingCurve::InCirc); + transition->addAnimation(animation); + + assignProperties(); +} + +DrawerStateMachine::~DrawerStateMachine() +{ +} + +void DrawerStateMachine::setOffset(int offset) +{ + m_offset = offset; + + QWidget *widget = m_drawer->parentWidget(); + if (widget) { + m_drawer->setGeometry(widget->rect().translated(offset, 0)); + } +} + +void DrawerStateMachine::assignProperties() +{ + m_closedState->assignProperty(this, "offset", -(m_drawer->width()+32)); + m_openState->assignProperty(this, "offset", 0); +} diff --git a/components/drawer_internal.h b/components/drawer_internal.h index 62de54d..35f1c45 100644 --- a/components/drawer_internal.h +++ b/components/drawer_internal.h @@ -1,4 +1,36 @@ #ifndef DRAWER_INTERNAL_H #define DRAWER_INTERNAL_H +#include + +class Drawer; + +class DrawerStateMachine : public QStateMachine +{ + Q_OBJECT + + Q_PROPERTY(int offset WRITE setOffset READ offset) + +public: + explicit DrawerStateMachine(Drawer *drawer); + ~DrawerStateMachine(); + + void setOffset(int offset); + inline int offset() const { return m_offset; } + + void assignProperties(); + +signals: + void enterOpenedState(); + void enterClosedState(); + +private: + Q_DISABLE_COPY(DrawerStateMachine) + + Drawer *const m_drawer; + QState *const m_openState; + QState *const m_closedState; + int m_offset; +}; + #endif // DRAWER_INTERNAL_H diff --git a/components/drawer_p.h b/components/drawer_p.h index 914bf15..7776363 100644 --- a/components/drawer_p.h +++ b/components/drawer_p.h @@ -1,9 +1,10 @@ #ifndef DRAWER_P_H #define DRAWER_P_H -#include +#include class Drawer; +class DrawerStateMachine; class DrawerPrivate { @@ -12,6 +13,11 @@ class DrawerPrivate public: DrawerPrivate(Drawer *q); + + Drawer *const q_ptr; + DrawerStateMachine *const stateMachine; + QWidget *const window; + int width; }; #endif // DRAWER_P_H diff --git a/lib/overlaywidget.cpp b/lib/overlaywidget.cpp index e69de29..7c57869 100644 --- a/lib/overlaywidget.cpp +++ b/lib/overlaywidget.cpp @@ -0,0 +1,57 @@ +#include "lib/overlaywidget.h" +#include + +OverlayWidget::OverlayWidget(QWidget *parent) + : QWidget(parent) +{ +} + +OverlayWidget::~OverlayWidget() +{ +} + +bool OverlayWidget::event(QEvent *event) +{ + switch (event->type()) + { + case QEvent::ParentChange: + { + if (!parent()) { + break; + } + parent()->installEventFilter(this); + setGeometry(overlayGeometry()); + break; + } + case QEvent::ParentAboutToChange: + { + if (!parent()) { + break; + } + parent()->removeEventFilter(this); + break; + } + default: + break; + } + return QWidget::event(event); +} + +bool OverlayWidget::eventFilter(QObject *obj, QEvent *event) +{ + const QEvent::Type type = event->type(); + + if (QEvent::Move == type || QEvent::Resize == type) { + setGeometry(overlayGeometry()); + } + return QWidget::eventFilter(obj, event); +} + +QRect OverlayWidget::overlayGeometry() const +{ + QWidget *widget = parentWidget(); + if (widget) { + return widget->rect(); + } + return QRect(); +} diff --git a/lib/overlaywidget.h b/lib/overlaywidget.h index 3a08ae4..b6e16a4 100644 --- a/lib/overlaywidget.h +++ b/lib/overlaywidget.h @@ -1,4 +1,24 @@ #ifndef OVERLAYWIDGET_H #define OVERLAYWIDGET_H +#include + +class OverlayWidget : public QWidget +{ + Q_OBJECT + +public: + explicit OverlayWidget(QWidget *parent = 0); + ~OverlayWidget(); + +protected: + bool event(QEvent *event) Q_DECL_OVERRIDE; + bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; + + virtual QRect overlayGeometry() const; + +private: + Q_DISABLE_COPY(OverlayWidget) +}; + #endif // OVERLAYWIDGET_H diff --git a/lib/testrippleoverlay.cpp b/lib/testrippleoverlay.cpp index e69de29..b9d2ef1 100644 --- a/lib/testrippleoverlay.cpp +++ b/lib/testrippleoverlay.cpp @@ -0,0 +1,86 @@ +#include "testrippleoverlay.h" +#include +#include +#include +#include + +TestRippleOverlay::TestRippleOverlay(QWidget *parent) + : QWidget(parent), + _animation(new QPropertyAnimation(this)), + _radius(0) +{ + _animation->setStartValue(0); + _animation->setEndValue(200); + _animation->setTargetObject(this); + _animation->setPropertyName("radius"); + _animation->setDuration(300); +} + +TestRippleOverlay::~TestRippleOverlay() +{ +} + +void TestRippleOverlay::setRadius(qreal radius) +{ + _radius = radius; + + refreshPixmap(); +} + +void TestRippleOverlay::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + + //QBrush brush; + //brush.setStyle(Qt::SolidPattern); + //brush.setColor(Qt::black); + //painter.setBrush(brush); + //painter.setOpacity(0.5); + //painter.setPen(Qt::NoPen); + + //painter.drawEllipse(_center, _radius, _radius); + + QPen pen; + pen.setColor(Qt::red); + pen.setWidth(5); + painter.setOpacity(1); + painter.setBrush(Qt::NoBrush); + painter.setPen(pen); + + painter.drawRect(rect()); + + painter.drawPixmap(0, 0, _pixmap); +} + +void TestRippleOverlay::mousePressEvent(QMouseEvent *event) +{ + qDebug() << event->pos(); + + _center = event->pos(); + _radius = 0; + + _animation->stop(); + _animation->start(); +} + +void TestRippleOverlay::refreshPixmap() +{ + _pixmap = QPixmap(size()); + _pixmap.fill(Qt::transparent); + + QPainter painter(&_pixmap); + painter.initFrom(this); + + QBrush brush; + brush.setStyle(Qt::SolidPattern); + brush.setColor(Qt::black); + painter.setBrush(brush); + painter.setOpacity(0.5); + painter.setPen(Qt::NoPen); + + painter.drawEllipse(_center, _radius, _radius); + + update(); +} diff --git a/lib/testrippleoverlay.h b/lib/testrippleoverlay.h index 43a3104..8124d07 100644 --- a/lib/testrippleoverlay.h +++ b/lib/testrippleoverlay.h @@ -1,4 +1,36 @@ #ifndef TESTRIPPLEOVERLAY_H #define TESTRIPPLEOVERLAY_H +#include + +class QPropertyAnimation; + +class TestRippleOverlay : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(qreal radius WRITE setRadius READ radius) + +public: + explicit TestRippleOverlay(QWidget *parent = 0); + ~TestRippleOverlay(); + + void setRadius(qreal radius); + inline qreal radius() const { return _radius; } + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + + void refreshPixmap(); + +private: + Q_DISABLE_COPY(TestRippleOverlay) + + QPropertyAnimation *_animation; + QPixmap _pixmap; + QPointF _center; + qreal _radius; +}; + #endif // TESTRIPPLEOVERLAY_H diff --git a/mainwindow.cpp b/mainwindow.cpp index fcfdc1a..97bf4b7 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -3,6 +3,8 @@ #include #include "components/flatbutton.h" #include "components/raisedbutton.h" +#include "components/drawer.h" +#include "lib/testrippleoverlay.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) @@ -20,10 +22,39 @@ MainWindow::MainWindow(QWidget *parent) RaisedButton *button; + //RaisedButton btn2(*button); + button = new RaisedButton; button->setText("Hello"); layout->addWidget(button); + + Drawer *drawer = new Drawer; + + connect(button, SIGNAL(clicked(bool)), drawer, SLOT(openDrawer())); + + drawer->setParent(this); + + QPushButton *btn1 = new QPushButton; + btn1->setText("Hello"); + + connect(btn1, SIGNAL(clicked(bool)), drawer, SLOT(openDrawer())); + + QPushButton *btn2 = new QPushButton; + btn2->setText("This is a button"); + + connect(btn2, SIGNAL(clicked(bool)), drawer, SLOT(closeDrawer())); + + layout = new QVBoxLayout; + layout->addWidget(btn1); + layout->addWidget(btn2); + layout->addStretch(); + + drawer->setDrawerLayout(layout); + + //TestRippleOverlay *overlay = new TestRippleOverlay; + //overlay->setParent(this); + } MainWindow::~MainWindow() @@ -38,7 +69,7 @@ void MainWindow::paintEvent(QPaintEvent *event) painter.setPen(Qt::red); - painter.drawRect(rect()); +// painter.drawRect(rect()); } /* diff --git a/qt-material-widgets.pro b/qt-material-widgets.pro index e85a381..3cca9ab 100644 --- a/qt-material-widgets.pro +++ b/qt-material-widgets.pro @@ -72,7 +72,10 @@ SOURCES += main.cpp\ components/scrollwidget_internal.cpp \ components/dialog_internal.cpp \ lib/transparencyproxy.cpp \ - lib/transparencyproxy_internal.cpp + lib/transparencyproxy_internal.cpp \ + components/drawer_internal.cpp \ + lib/testrippleoverlay.cpp \ + lib/overlaywidget.cpp HEADERS += mainwindow.h \ components/appbar.h \ @@ -159,7 +162,10 @@ HEADERS += mainwindow.h \ lib/transparencyproxy.h \ lib/transparencyproxy_p.h \ lib/transparencyproxy_internal.h \ - components/drawer_p.h + components/drawer_p.h \ + components/drawer_internal.h \ + lib/testrippleoverlay.h \ + lib/overlaywidget.h RESOURCES += \ resources.qrc