implement Snackbar

This commit is contained in:
laserpants 2016-06-18 11:50:33 +03:00
parent 5d0f4fd535
commit 8d25f4036a
11 changed files with 479 additions and 7 deletions

View File

@ -13,6 +13,7 @@
- [x] Radio Button
- [x] Raised Button
- [x] Slider
- [x] Snackbar
- [x] Tabs
- [x] Text Field
- [x] Toggle
@ -25,7 +26,6 @@
- [ ] Progress
- [ ] Select Field
- [ ] Scroll Bar
- [ ] Snackbar
#### Not started

View File

@ -31,6 +31,10 @@ void BadgePrivate::init()
q->setFont(font);
q->setText("+1");
if (q->parentWidget()) {
q->parentWidget()->installEventFilter(q);
}
}
Badge::Badge(QWidget *parent)

View File

@ -30,6 +30,10 @@ void FloatingActionButtonPrivate::init()
path.addEllipse(0, 0, 56, 56);
ripple->setClipPath(path);
ripple->setClipping(true);
if (q->parentWidget()) {
q->parentWidget()->installEventFilter(q);
}
}
QRect FloatingActionButtonPrivate::fabGeometry() const

View File

@ -1,10 +1,245 @@
#include "snackbar.h"
#include <QPainter>
#include <QEvent>
#include <QDebug>
#include "snackbar_p.h"
#include "snackbar_internal.h"
SnackbarPrivate::SnackbarPrivate(Snackbar *q)
: q_ptr(q),
duration(3000),
boxWidth(300)
{
}
SnackbarPrivate::~SnackbarPrivate()
{
}
void SnackbarPrivate::init()
{
Q_Q(Snackbar);
machine = new SnackbarStateMachine(q);
q->setAttribute(Qt::WA_TransparentForMouseEvents);
QFont font(q->font());
font.setPointSizeF(11);
q->setFont(font);
backgroundColor = QColor(0, 0, 0, 220);
textColor = Qt::white;
if (q->parentWidget()) {
q->parentWidget()->installEventFilter(q);
}
machine->start();
}
Snackbar::Snackbar(QWidget *parent)
: QWidget(parent)
: QWidget(parent),
d_ptr(new SnackbarPrivate(this))
{
d_func()->init();
}
Snackbar::~Snackbar()
{
}
void Snackbar::setAutoHideDuration(int duration)
{
Q_D(Snackbar);
d->duration = duration;
}
int Snackbar::autoHideDuration() const
{
Q_D(const Snackbar);
return d->duration;
}
void Snackbar::setBackgroundColor(const QColor &color)
{
Q_D(Snackbar);
d->backgroundColor = color;
}
QColor Snackbar::backgroundColor() const
{
Q_D(const Snackbar);
return d->backgroundColor;
}
void Snackbar::setTextColor(const QColor &color)
{
Q_D(Snackbar);
d->textColor = color;
}
QColor Snackbar::textColor() const
{
Q_D(const Snackbar);
return d->textColor;
}
void Snackbar::setFontSize(qreal size)
{
Q_D(Snackbar);
QFont f(font());
f.setPointSizeF(size);
setFont(f);
update();
}
qreal Snackbar::fontSize() const
{
Q_D(const Snackbar);
return font().pointSizeF();
}
void Snackbar::setBoxWidth(int width)
{
Q_D(Snackbar);
d->boxWidth = width;
update();
}
int Snackbar::boxWidth() const
{
Q_D(const Snackbar);
return d->boxWidth;
}
void Snackbar::addMessage(const QString &message, bool instant)
{
Q_D(Snackbar);
if (instant && !d->messages.isEmpty()) {
d->messages.insert(1, message);
} else {
d->messages.push_back(message);
}
if (instant) {
emit d->machine->hideSnackbar();
} else {
emit d->machine->showSnackbar();
}
}
bool Snackbar::event(QEvent *event)
{
switch (event->type())
{
case QEvent::ParentChange:
{
if (!parent())
break;
parent()->installEventFilter(this);
QWidget *widget;
if ((widget = parentWidget())) {
setGeometry(widget->rect());
}
break;
}
case QEvent::ParentAboutToChange:
{
if (!parent())
break;
parent()->removeEventFilter(this);
break;
}
default:
break;
}
return QWidget::event(event);
}
bool Snackbar::eventFilter(QObject *obj, QEvent *event)
{
QEvent::Type type = event->type();
if (QEvent::Move == type || QEvent::Resize == type)
{
QWidget *widget;
if ((widget = parentWidget())) {
setGeometry(widget->rect());
}
}
return QWidget::eventFilter(obj, event);
}
void Snackbar::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
Q_D(Snackbar);
if (d->messages.isEmpty()) {
return;
}
QString message = d->messages.first();
QPainter painter(this);
QRectF r = painter.boundingRect(QRect((width()-d->boxWidth)/2, 0, d->boxWidth, 40),
Qt::AlignCenter | Qt::TextWordWrap,
message);
painter.setRenderHint(QPainter::Antialiasing);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(d->backgroundColor);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
QRectF s((width()-d->boxWidth)/2, 0, d->boxWidth, 40);
s = r.united(s);
const int yOffs = height()-s.height()-s.top()
+ static_cast<qreal>(s.height()+20)*d->machine->offset();
s.translate(0, yOffs);
r.translate(0, yOffs-5);
painter.drawRoundedRect(s.adjusted(-10, -10, 10, 10), 3, 3);
painter.setPen(d->textColor);
painter.drawText(r, Qt::AlignCenter | Qt::TextWordWrap, message, &r);
}
void Snackbar::dequeue()
{
Q_D(Snackbar);
if (d->messages.isEmpty()) {
return;
}
d->messages.removeFirst();
if (!d->messages.isEmpty()) {
emit d->machine->showNextSnackbar();
} else {
emit d->machine->waitForSnackbar();
}
}

View File

@ -3,17 +3,51 @@
#include <QWidget>
class SnackbarPrivate;
class SnackbarStateMachine;
class Snackbar : public QWidget
{
Q_OBJECT
Q_PROPERTY(int autoHideDuration WRITE setAutoHideDuration READ autoHideDuration)
public:
explicit Snackbar(QWidget *parent = 0);
~Snackbar();
void setAutoHideDuration(int duration);
int autoHideDuration() const;
void setBackgroundColor(const QColor &color);
QColor backgroundColor() const;
void setTextColor(const QColor &color);
QColor textColor() const;
void setFontSize(qreal size);
qreal fontSize() const;
void setBoxWidth(int width);
int boxWidth() const;
public slots:
void addMessage(const QString &message, bool instant = false);
protected:
bool event(QEvent *event) Q_DECL_OVERRIDE;
bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
void dequeue();
const QScopedPointer<SnackbarPrivate> d_ptr;
private:
Q_DISABLE_COPY(Snackbar)
//Q_DECLARE_PRIVATE(Snackbar)
Q_DECLARE_PRIVATE(Snackbar)
friend class SnackbarStateMachine;
};
#endif // SNACKBAR_H

View File

@ -0,0 +1,89 @@
#include "snackbar_internal.h"
#include <QSignalTransition>
#include <QPropertyAnimation>
#include <QTimer>
#include "snackbar.h"
SnackbarStateMachine::SnackbarStateMachine(Snackbar *parent)
: QStateMachine(parent),
snackbar(parent),
_offset(0)
{
timer.setSingleShot(true);
QState *hiddenState = new QState;
QState *visibleState = new QState;
QState *finalState = new QState;
addState(hiddenState);
addState(visibleState);
addState(finalState);
setInitialState(hiddenState);
QSignalTransition *transition;
transition = new QSignalTransition(this, SIGNAL(showSnackbar()));
transition->setTargetState(visibleState);
hiddenState->addTransition(transition);
transition = new QSignalTransition(this, SIGNAL(hideSnackbar()));
transition->setTargetState(visibleState);
hiddenState->addTransition(transition);
transition = new QSignalTransition(this, SIGNAL(hideSnackbar()));
transition->setTargetState(finalState);
visibleState->addTransition(transition);
transition = new QSignalTransition(this, SIGNAL(waitForSnackbar()));
transition->setTargetState(hiddenState);
finalState->addTransition(transition);
transition = new QSignalTransition(this, SIGNAL(showNextSnackbar()));
transition->setTargetState(visibleState);
finalState->addTransition(transition);
connect(visibleState, SIGNAL(propertiesAssigned()),
this, SLOT(snackbarShown()));
connect(finalState, SIGNAL(propertiesAssigned()),
this, SLOT(snackbarHidden()));
QPropertyAnimation *animation;
animation = new QPropertyAnimation(this, "offset");
animation->setEasingCurve(QEasingCurve::OutCubic);
animation->setDuration(400);
addDefaultAnimation(animation);
hiddenState->assignProperty(this, "offset", 1);
visibleState->assignProperty(this, "offset", 0);
finalState->assignProperty(this, "offset", 1);
connect(&timer, SIGNAL(timeout()), this, SIGNAL(hideSnackbar()));
connect(this, SIGNAL(hideSnackbar()), &timer, SLOT(stop()));
}
SnackbarStateMachine::~SnackbarStateMachine()
{
}
void SnackbarStateMachine::setOffset(qreal offset)
{
_offset = offset;
snackbar->update();
}
qreal SnackbarStateMachine::offset() const
{
return _offset;
}
void SnackbarStateMachine::snackbarHidden()
{
snackbar->dequeue();
}
void SnackbarStateMachine::snackbarShown()
{
timer.start(snackbar->autoHideDuration());
}

View File

@ -0,0 +1,40 @@
#ifndef SNACKBAR_INTERNAL_H
#define SNACKBAR_INTERNAL_H
#include <QStateMachine>
#include <QTimer>
class Snackbar;
class SnackbarStateMachine : public QStateMachine
{
Q_OBJECT
Q_PROPERTY(qreal offset WRITE setOffset READ offset)
public:
SnackbarStateMachine(Snackbar *parent);
~SnackbarStateMachine();
void setOffset(qreal offset);
qreal offset() const;
protected slots:
void snackbarHidden();
void snackbarShown();
signals:
void showSnackbar();
void hideSnackbar();
void waitForSnackbar();
void showNextSnackbar();
private:
Q_DISABLE_COPY(SnackbarStateMachine)
Snackbar *const snackbar;
QTimer timer;
qreal _offset;
};
#endif // SNACKBAR_INTERNAL_H

View File

@ -1,4 +1,29 @@
#ifndef SNACKBAR_P_H
#define SNACKBAR_P_H
#include <QObject>
class Snackbar;
class SnackbarStateMachine;
class SnackbarPrivate
{
Q_DISABLE_COPY(SnackbarPrivate)
Q_DECLARE_PUBLIC(Snackbar)
public:
SnackbarPrivate(Snackbar *q);
~SnackbarPrivate();
void init();
Snackbar *const q_ptr;
SnackbarStateMachine *machine;
QColor backgroundColor;
QColor textColor;
QList<QString> messages;
int duration;
int boxWidth;
};
#endif // SNACKBAR_P_H

View File

@ -2,6 +2,7 @@
#include <QMenu>
#include <QMenuBar>
#include <QStackedLayout>
#include <QStringBuilder>
#include <QDebug>
#include "mainwindow.h"
#include "examples/about.h"
@ -21,6 +22,7 @@
#include "examples/menuexamples.h"
#include "examples/iconmenuexamples.h"
#include "components/fab.h"
#include "components/snackbar.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
@ -54,10 +56,28 @@ MainWindow::MainWindow(QWidget *parent)
//
FloatingActionButton *button2 = new FloatingActionButton(QIcon("../qt-material-widgets/ic_message_white_24px.svg"));
button2->setParent(this);
new FloatingActionButton(QIcon("../qt-material-widgets/ic_message_white_24px.svg"), this);
//button2->setDisabled(true);
snackbar = new Snackbar;
snackbar->setParent(this);
//
QPushButton *btn = new QPushButton;
btn->setText("Show Snackbar");
btn->setGeometry(90, 50, 140, 40);
btn->setParent(this);
connect(btn, SIGNAL(pressed()), this, SLOT(addMsg()));
btn = new QPushButton;
btn->setText("Show Snackbar (instant)");
btn->setGeometry(240, 50, 140, 40);
btn->setParent(this);
connect(btn, SIGNAL(pressed()), this, SLOT(addInstantMsg()));
}
MainWindow::~MainWindow()
@ -102,6 +122,21 @@ void MainWindow::showWidget(QAction *action)
}
}
static int n = 1;
void MainWindow::addMsg()
{
snackbar->addMessage(QString("Hello from the Snackbar (") % QString::number(n++) % QString(")"));
}
void MainWindow::addInstantMsg()
{
QString msg("This is longer message which will show up immediately after it is added to the message queue");
msg.append(QString(" (") % QString::number(n++) % QString(")."));
snackbar->addMessage(msg, true);
}
void MainWindow::_initWidget()
{
QWidget *widget = new QWidget;

View File

@ -20,6 +20,7 @@ class AvatarExamples;
class MenuExamples;
class IconMenuExamples;
class QStackedLayout;
class Snackbar;
class MainWindow : public QMainWindow
{
@ -31,6 +32,8 @@ public:
protected slots:
void showWidget(QAction *action);
void addMsg();
void addInstantMsg();
private:
void _initWidget();
@ -53,6 +56,7 @@ private:
MenuExamples *const _menuExamples;
IconMenuExamples *const _iconMenuExamples;
About *const _about;
Snackbar *snackbar;
};
#endif // MAINWINDOW_H

View File

@ -64,7 +64,8 @@ SOURCES += main.cpp\
lib/checkable_internal.cpp \
components/snackbar.cpp \
components/textfield_internal.cpp \
components/drawer.cpp
components/drawer.cpp \
components/snackbar_internal.cpp
HEADERS += mainwindow.h \
components/appbar.h \
@ -137,7 +138,8 @@ HEADERS += mainwindow.h \
components/textfield_internal.h \
components/badge_p.h \
components/drawer.h \
components/avatar_p.h
components/avatar_p.h \
components/snackbar_internal.h
RESOURCES += \
resources.qrc