Add spinner to hide uninitialized layout after login

This commit is contained in:
Konstantinos Sideris 2017-04-26 02:23:12 +03:00
parent 58936cd67b
commit 415ef7e9c7
9 changed files with 494 additions and 9 deletions

View File

@ -100,7 +100,9 @@ set(SRC_FILES
src/ui/Avatar.cc
src/ui/Badge.cc
src/ui/CircularProgress.cc
src/ui/FlatButton.cc
src/ui/OverlayModal.cc
src/ui/RaisedButton.cc
src/ui/Ripple.cc
src/ui/RippleOverlay.cc
@ -142,6 +144,7 @@ qt5_wrap_cpp(MOC_HEADERS
include/ui/Avatar.h
include/ui/Badge.h
include/ui/CircularProgress.h
include/ui/FlatButton.h
include/ui/OverlayWidget.h
include/ui/RaisedButton.h

View File

@ -22,8 +22,10 @@
#include <QSharedPointer>
#include "ChatPage.h"
#include "CircularProgress.h"
#include "LoginPage.h"
#include "MatrixClient.h"
#include "OverlayModal.h"
#include "RegisterPage.h"
#include "SlidingStackWidget.h"
#include "WelcomePage.h"
@ -41,7 +43,7 @@ public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
private slots:
// Show the welcome page in the main window.
void showWelcomePage();
@ -54,6 +56,8 @@ public slots:
// Show the chat page and start communicating with the given access token.
void showChatPage(QString user_id, QString home_server, QString token);
void removeOverlayProgressBar();
private:
// The UI component of the main window.
Ui::MainWindow *ui_;
@ -73,6 +77,10 @@ private:
// The main chat area.
ChatPage *chat_page_;
// Used to hide undefined states between page transitions.
OverlayModal *progress_modal_;
CircularProgress *spinner_;
// Matrix Client API provider.
QSharedPointer<MatrixClient> client_;
};

View File

@ -0,0 +1,114 @@
#ifndef UI_CIRCULAR_PROGRESS_H
#define UI_CIRCULAR_PROGRESS_H
#include <QObject>
#include <QProgressBar>
#include "Theme.h"
class CircularProgressDelegate;
class CircularProgress : public QProgressBar
{
Q_OBJECT
Q_PROPERTY(qreal lineWidth WRITE setLineWidth READ lineWidth)
Q_PROPERTY(qreal size WRITE setSize READ size)
Q_PROPERTY(QColor color WRITE setColor READ color)
public:
explicit CircularProgress(QWidget *parent = nullptr);
~CircularProgress();
void setProgressType(ui::ProgressType type);
void setLineWidth(qreal width);
void setSize(int size);
void setColor(const QColor &color);
ui::ProgressType progressType() const;
qreal lineWidth() const;
int size() const;
QColor color() const;
QSize sizeHint() const override;
protected:
void paintEvent(QPaintEvent *event) override;
private:
CircularProgressDelegate *delegate_;
ui::ProgressType progress_type_;
QColor color_;
// Circle width.
qreal width_;
// Circle radius.
int size_;
};
class CircularProgressDelegate : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal dashOffset WRITE setDashOffset READ dashOffset)
Q_PROPERTY(qreal dashLength WRITE setDashLength READ dashLength)
Q_PROPERTY(int angle WRITE setAngle READ angle)
public:
explicit CircularProgressDelegate(CircularProgress *parent);
~CircularProgressDelegate();
inline void setDashOffset(qreal offset);
inline void setDashLength(qreal length);
inline void setAngle(int angle);
inline qreal dashOffset() const;
inline qreal dashLength() const;
inline int angle() const;
private:
CircularProgress *const progress_;
qreal dash_offset_;
qreal dash_length_;
int angle_;
};
inline void CircularProgressDelegate::setDashOffset(qreal offset)
{
dash_offset_ = offset;
progress_->update();
}
inline void CircularProgressDelegate::setDashLength(qreal length)
{
dash_length_ = length;
progress_->update();
}
inline void CircularProgressDelegate::setAngle(int angle)
{
angle_ = angle;
progress_->update();
}
inline qreal CircularProgressDelegate::dashOffset() const
{
return dash_offset_;
}
inline qreal CircularProgressDelegate::dashLength() const
{
return dash_length_;
}
inline int CircularProgressDelegate::angle() const
{
return angle_;
}
#endif // UI_CIRCULAR_PROGRESS_H

60
include/ui/OverlayModal.h Normal file
View File

@ -0,0 +1,60 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UI_OVERLAY_MODAL_H
#define UI_OVERLAY_MODAL_H
#include <QGraphicsOpacityEffect>
#include <QPaintEvent>
#include <QPropertyAnimation>
#include "OverlayWidget.h"
class OverlayModal : public OverlayWidget
{
public:
explicit OverlayModal(QWidget *parent, QWidget *content);
void fadeIn();
void fadeOut();
public:
inline void setDuration(int duration);
inline void setColor(QColor color);
protected:
void paintEvent(QPaintEvent *event) override;
private:
int duration_;
QColor color_;
QGraphicsOpacityEffect *opacity_;
QPropertyAnimation *animation_;
};
inline void OverlayModal::setDuration(int duration)
{
duration_ = duration;
}
inline void OverlayModal::setColor(QColor color)
{
color_ = color;
}
#endif // UI_OVERLAY_MODAL_H

View File

@ -2,7 +2,6 @@
#define UI_OVERLAY_WIDGET_H
#include <QEvent>
#include <QObject>
#include <QWidget>
class OverlayWidget : public QWidget
@ -10,8 +9,7 @@ class OverlayWidget : public QWidget
Q_OBJECT
public:
explicit OverlayWidget(QWidget *parent = 0);
~OverlayWidget();
explicit OverlayWidget(QWidget *parent = nullptr);
protected:
bool event(QEvent *event) override;

View File

@ -25,6 +25,8 @@
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui_(new Ui::MainWindow)
, progress_modal_{nullptr}
, spinner_{nullptr}
{
ui_->setupUi(this);
client_ = QSharedPointer<MatrixClient>(new MatrixClient("matrix.org"));
@ -53,12 +55,40 @@ MainWindow::MainWindow(QWidget *parent)
connect(chat_page_, SIGNAL(close()), this, SLOT(showWelcomePage()));
connect(chat_page_, SIGNAL(changeWindowTitle(QString)), this, SLOT(setWindowTitle(QString)));
connect(client_.data(),
SIGNAL(initialSyncCompleted(const SyncResponse &)),
this,
SLOT(removeOverlayProgressBar()));
connect(client_.data(),
SIGNAL(loginSuccess(QString, QString, QString)),
this,
SLOT(showChatPage(QString, QString, QString)));
}
void MainWindow::removeOverlayProgressBar()
{
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]() {
timer->deleteLater();
if (progress_modal_ != nullptr) {
progress_modal_->deleteLater();
progress_modal_->fadeOut();
}
if (progress_modal_ != nullptr)
spinner_->deleteLater();
progress_modal_ = nullptr;
spinner_ = nullptr;
});
timer->start(500);
}
void MainWindow::showChatPage(QString userid, QString homeserver, QString token)
{
QSettings settings;
@ -69,6 +99,17 @@ void MainWindow::showChatPage(QString userid, QString homeserver, QString token)
int index = sliding_stack_->getWidgetIndex(chat_page_);
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
if (spinner_ == nullptr) {
spinner_ = new CircularProgress(this);
spinner_->setColor("#acc7dc");
spinner_->setSize(100);
}
if (progress_modal_ == nullptr) {
progress_modal_ = new OverlayModal(this, spinner_);
progress_modal_->fadeIn();
}
login_page_->reset();
chat_page_->bootstrap(userid, homeserver, token);
}

190
src/ui/CircularProgress.cc Normal file
View File

@ -0,0 +1,190 @@
#include <QPainter>
#include <QParallelAnimationGroup>
#include <QPen>
#include <QPropertyAnimation>
#include "CircularProgress.h"
#include "Theme.h"
CircularProgress::CircularProgress(QWidget *parent)
: QProgressBar{parent}
, progress_type_{ui::ProgressType::IndeterminateProgress}
, width_{6.25}
, size_{64}
{
delegate_ = new CircularProgressDelegate(this);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
auto group = new QParallelAnimationGroup(this);
group->setLoopCount(-1);
auto length_animation = new QPropertyAnimation(this);
length_animation->setPropertyName("dashLength");
length_animation->setTargetObject(delegate_);
length_animation->setEasingCurve(QEasingCurve::InOutQuad);
length_animation->setStartValue(0.1);
length_animation->setKeyValueAt(0.15, 0.2);
length_animation->setKeyValueAt(0.6, 20);
length_animation->setKeyValueAt(0.7, 20);
length_animation->setEndValue(20);
length_animation->setDuration(2050);
auto offset_animation = new QPropertyAnimation(this);
offset_animation->setPropertyName("dashOffset");
offset_animation->setTargetObject(delegate_);
offset_animation->setEasingCurve(QEasingCurve::InOutSine);
offset_animation->setStartValue(0);
offset_animation->setKeyValueAt(0.15, 0);
offset_animation->setKeyValueAt(0.6, -7);
offset_animation->setKeyValueAt(0.7, -7);
offset_animation->setEndValue(-25);
offset_animation->setDuration(2050);
auto angle_animation = new QPropertyAnimation(this);
angle_animation->setPropertyName("angle");
angle_animation->setTargetObject(delegate_);
angle_animation->setStartValue(0);
angle_animation->setEndValue(719);
angle_animation->setDuration(2050);
group->addAnimation(length_animation);
group->addAnimation(offset_animation);
group->addAnimation(angle_animation);
group->start();
}
void CircularProgress::setProgressType(ui::ProgressType type)
{
progress_type_ = type;
update();
}
void CircularProgress::setLineWidth(qreal width)
{
width_ = width;
update();
updateGeometry();
}
void CircularProgress::setSize(int size)
{
size_ = size;
update();
updateGeometry();
}
ui::ProgressType CircularProgress::progressType() const
{
return progress_type_;
}
qreal CircularProgress::lineWidth() const
{
return width_;
}
int CircularProgress::size() const
{
return size_;
}
void CircularProgress::setColor(const QColor &color)
{
color_ = color;
}
QColor CircularProgress::color() const
{
if (!color_.isValid()) {
return QColor("red");
}
return color_;
}
QSize CircularProgress::sizeHint() const
{
const qreal s = size_ + width_ + 8;
return QSize(s, s);
}
void CircularProgress::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
/*
* If the progress bar is disabled draw an X instead
*/
if (!isEnabled()) {
QPen pen;
pen.setCapStyle(Qt::RoundCap);
pen.setWidthF(lineWidth());
pen.setColor("gray");
auto center = rect().center();
painter.setPen(pen);
painter.drawLine(center - QPointF(20, 20), center + QPointF(20, 20));
painter.drawLine(center + QPointF(20, -20), center - QPointF(20, -20));
return;
}
if (progress_type_ == ui::ProgressType::IndeterminateProgress) {
painter.translate(width() / 2, height() / 2);
painter.rotate(delegate_->angle());
}
QPen pen;
pen.setCapStyle(Qt::RoundCap);
pen.setWidthF(width_);
pen.setColor(color());
if (ui::IndeterminateProgress == progress_type_) {
QVector<qreal> pattern;
pattern << delegate_->dashLength() * size_ / 50 << 30 * size_ / 50;
pen.setDashOffset(delegate_->dashOffset() * size_ / 50);
pen.setDashPattern(pattern);
painter.setPen(pen);
painter.drawEllipse(QPoint(0, 0), size_ / 2, size_ / 2);
} else {
painter.setPen(pen);
const qreal x = (width() - size_) / 2;
const qreal y = (height() - size_) / 2;
const qreal a = 360 * (value() - minimum()) / (maximum() - minimum());
QPainterPath path;
path.arcMoveTo(x, y, size_, size_, 0);
path.arcTo(x, y, size_, size_, 0, a);
painter.drawPath(path);
}
}
CircularProgress::~CircularProgress()
{
}
CircularProgressDelegate::CircularProgressDelegate(CircularProgress *parent)
: QObject(parent)
, progress_(parent)
, dash_offset_(0)
, dash_length_(89)
, angle_(0)
{
Q_ASSERT(parent);
}
CircularProgressDelegate::~CircularProgressDelegate()
{
}

72
src/ui/OverlayModal.cc Normal file
View File

@ -0,0 +1,72 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include <QPainter>
#include <QVBoxLayout>
#include "OverlayModal.h"
OverlayModal::OverlayModal(QWidget *parent, QWidget *content)
: OverlayWidget(parent)
, duration_{500}
, color_{QColor(55, 55, 55)}
{
setAttribute(Qt::WA_TranslucentBackground);
auto layout = new QVBoxLayout();
layout->addWidget(content);
layout->setAlignment(Qt::AlignCenter);
setLayout(layout);
opacity_ = new QGraphicsOpacityEffect(this);
setGraphicsEffect(opacity_);
opacity_->setOpacity(1);
animation_ = new QPropertyAnimation(opacity_, "opacity", this);
animation_->setStartValue(1);
animation_->setEndValue(0);
animation_->setDuration(duration_);
animation_->setEasingCurve(QEasingCurve::Linear);
connect(animation_, &QPropertyAnimation::finished, [this]() {
if (animation_->direction() == QAbstractAnimation::Forward)
this->close();
});
}
void OverlayModal::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.fillRect(rect(), color_);
}
void OverlayModal::fadeIn()
{
animation_->setDirection(QAbstractAnimation::Backward);
animation_->start();
show();
}
void OverlayModal::fadeOut()
{
animation_->setDirection(QAbstractAnimation::Forward);
animation_->start();
}

View File

@ -4,12 +4,11 @@
OverlayWidget::OverlayWidget(QWidget *parent)
: QWidget(parent)
{
if (parent)
if (parent) {
parent->installEventFilter(this);
}
OverlayWidget::~OverlayWidget()
{
setGeometry(overlayGeometry());
raise();
}
}
bool OverlayWidget::event(QEvent *event)