implement Snackbar
This commit is contained in:
parent
5d0f4fd535
commit
8d25f4036a
|
@ -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
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ void BadgePrivate::init()
|
|||
q->setFont(font);
|
||||
|
||||
q->setText("+1");
|
||||
|
||||
if (q->parentWidget()) {
|
||||
q->parentWidget()->installEventFilter(q);
|
||||
}
|
||||
}
|
||||
|
||||
Badge::Badge(QWidget *parent)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue