diff --git a/CMakeLists.txt b/CMakeLists.txt index 99ae1d62..6415b912 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ set(SRC_FILES src/ui/FlatButton.cc src/ui/OverlayModal.cc src/ui/ScrollBar.cc + src/ui/SnackBar.cc src/ui/RaisedButton.cc src/ui/Ripple.cc src/ui/RippleOverlay.cc @@ -249,6 +250,7 @@ qt5_wrap_cpp(MOC_HEADERS include/ui/FlatButton.h include/ui/OverlayWidget.h include/ui/ScrollBar.h + include/ui/SnackBar.h include/ui/RaisedButton.h include/ui/Ripple.h include/ui/RippleOverlay.h @@ -292,9 +294,6 @@ if (APPLE) endif() if (BUILD_TESTS) - # - # Build tests. - # enable_testing() find_package(GTest REQUIRED) @@ -312,26 +311,23 @@ if (BUILD_TESTS) add_test(MatrixEvents events_test) add_test(MatrixEventCollection event_collection_test) add_test(MatrixMessageEvents message_events) -else() - # - # Build the executable. - # +endif() + if(APPVEYOR_BUILD) set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network lmdb) else() set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network ${LMDB_LIBRARY}) endif() - set (NHEKO_DEPS ${OS_BUNDLE} ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC} ${LANG_QRC} ${QM_SRC}) +set (NHEKO_DEPS ${OS_BUNDLE} ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC} ${LANG_QRC} ${QM_SRC}) - if(APPLE) - add_executable (nheko ${NHEKO_DEPS}) - target_link_libraries (nheko ${NHEKO_LIBS} Qt5::MacExtras) - elseif(WIN32) - add_executable (nheko ${ICON_FILE} ${NHEKO_DEPS}) - target_link_libraries (nheko ${NTDLIB} ${NHEKO_LIBS} Qt5::WinMain) - else() - add_executable (nheko ${NHEKO_DEPS}) - target_link_libraries (nheko ${NHEKO_LIBS}) - endif() +if(APPLE) + add_executable (nheko ${NHEKO_DEPS}) + target_link_libraries (nheko ${NHEKO_LIBS} Qt5::MacExtras) +elseif(WIN32) + add_executable (nheko ${ICON_FILE} ${NHEKO_DEPS}) + target_link_libraries (nheko ${NTDLIB} ${NHEKO_LIBS} Qt5::WinMain) +else() + add_executable (nheko ${NHEKO_DEPS}) + target_link_libraries (nheko ${NHEKO_LIBS}) endif() diff --git a/include/ui/SnackBar.h b/include/ui/SnackBar.h new file mode 100644 index 00000000..076b7105 --- /dev/null +++ b/include/ui/SnackBar.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +#include "OverlayWidget.h" + +enum class SnackBarPosition { + Bottom, + Top, +}; + +class SnackBar : public OverlayWidget +{ + Q_OBJECT + +public: + explicit SnackBar(QWidget *parent); + ~SnackBar(); + + inline void setBackgroundColor(const QColor &color); + inline void setTextColor(const QColor &color); + inline void setPosition(SnackBarPosition pos); + +public slots: + void showMessage(const QString &msg); + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + +private slots: + void onTimeout(); + void hideMessage(); + +private: + void stopTimers(); + void start(); + + QColor bgColor_; + QColor textColor_; + + qreal bgOpacity_; + qreal offset_; + + QList messages_; + + QTimer *showTimer_; + QTimer *hideTimer_; + + int duration_; + int boxWidth_; + int boxHeight_; + int boxPadding_; + + SnackBarPosition position_; +}; + +inline void +SnackBar::setPosition(SnackBarPosition pos) +{ + position_ = pos; + update(); +} + +inline void +SnackBar::setBackgroundColor(const QColor &color) +{ + bgColor_ = color; + update(); +} + +inline void +SnackBar::setTextColor(const QColor &color) +{ + textColor_ = color; + update(); +} diff --git a/src/ui/SnackBar.cc b/src/ui/SnackBar.cc new file mode 100644 index 00000000..673c2f93 --- /dev/null +++ b/src/ui/SnackBar.cc @@ -0,0 +1,143 @@ +#include +#include + +#include "SnackBar.h" + +constexpr int STARTING_OFFSET = 1; + +SnackBar::SnackBar(QWidget *parent) + : OverlayWidget(parent) +{ + bgOpacity_ = 0.9; + duration_ = 6000; + boxWidth_ = 400; + boxHeight_ = 40; + boxPadding_ = 10; + textColor_ = QColor("white"); + bgColor_ = QColor("#333"); + offset_ = STARTING_OFFSET; + position_ = SnackBarPosition::Top; + + QFont font("Open Sans", 14, QFont::Medium); + setFont(font); + + showTimer_ = new QTimer(); + hideTimer_ = new QTimer(); + hideTimer_->setSingleShot(true); + + connect(showTimer_, SIGNAL(timeout()), this, SLOT(onTimeout())); + connect(hideTimer_, SIGNAL(timeout()), this, SLOT(hideMessage())); +} + +SnackBar::~SnackBar() +{ + stopTimers(); + + delete showTimer_; + delete hideTimer_; +} + +void +SnackBar::start() +{ + show(); + raise(); + + showTimer_->start(10); +} + +void +SnackBar::hideMessage() +{ + stopTimers(); + hide(); + + // Moving on to the next message. + messages_.removeFirst(); + + // Reseting the starting position of the widget. + offset_ = STARTING_OFFSET; + + if (!messages_.isEmpty()) + start(); +} + +void +SnackBar::stopTimers() +{ + showTimer_->stop(); + hideTimer_->stop(); +} + +void +SnackBar::showMessage(const QString &msg) +{ + messages_.push_back(msg); + + // There is already an active message. + if (isVisible()) + return; + + start(); +} + +void +SnackBar::onTimeout() +{ + offset_ -= 0.5; + + if (offset_ <= 0.0) { + showTimer_->stop(); + hideTimer_->start(duration_); + } + + update(); +} + +void +SnackBar::mousePressEvent(QMouseEvent *) +{ + hideMessage(); +} + +void +SnackBar::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + if (messages_.isEmpty()) + return; + + auto message_ = messages_.first(); + + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + QBrush brush; + brush.setStyle(Qt::SolidPattern); + brush.setColor(bgColor_); + p.setBrush(brush); + p.setOpacity(bgOpacity_); + + QRect r(0, 0, boxWidth_, boxHeight_); + + p.setPen(Qt::white); + QRect br = p.boundingRect(r, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, message_); + + p.setPen(Qt::NoPen); + r = br.united(r).adjusted(-boxPadding_, -boxPadding_, boxPadding_, boxPadding_); + + const qreal s = 1 - offset_; + + if (position_ == SnackBarPosition::Bottom) + p.translate((width() - (r.width() - 2 * boxPadding_)) / 2, + height() - boxPadding_ - s * (r.height())); + else + p.translate((width() - (r.width() - 2 * boxPadding_)) / 2, + s * (r.height()) - 2 * boxPadding_); + + br.moveCenter(r.center()); + p.drawRoundedRect(r.adjusted(0, 0, 0, 3), 3, 3); + p.setPen(textColor_); + p.drawText(br, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, message_); +}