[Qt] add out-of-sync modal info layer

This commit is contained in:
Jonas Schnelli 2016-07-19 15:51:24 +02:00
parent e47052f6b5
commit e3245b43d5
No known key found for this signature in database
GPG key ID: 29D4BCB6416F53EC
6 changed files with 602 additions and 1 deletions

View file

@ -96,6 +96,7 @@ QT_FORMS_UI = \
qt/forms/editaddressdialog.ui \
qt/forms/helpmessagedialog.ui \
qt/forms/intro.ui \
qt/forms/modaloverlay.ui \
qt/forms/openuridialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/overviewpage.ui \
@ -125,6 +126,7 @@ QT_MOC_CPP = \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
qt/moc_macnotificationhandler.cpp \
qt/moc_modaloverlay.cpp \
qt/moc_notificator.cpp \
qt/moc_openuridialog.cpp \
qt/moc_optionsdialog.cpp \
@ -192,6 +194,7 @@ BITCOIN_QT_H = \
qt/intro.h \
qt/macdockiconhandler.h \
qt/macnotificationhandler.h \
qt/modaloverlay.h \
qt/networkstyle.h \
qt/notificator.h \
qt/openuridialog.h \
@ -292,6 +295,7 @@ BITCOIN_QT_CPP = \
qt/csvmodelwriter.cpp \
qt/guiutil.cpp \
qt/intro.cpp \
qt/modaloverlay.cpp \
qt/networkstyle.cpp \
qt/notificator.cpp \
qt/optionsdialog.cpp \

View file

@ -12,6 +12,7 @@
#include "clientmodel.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "modaloverlay.h"
#include "networkstyle.h"
#include "notificator.h"
#include "openuridialog.h"
@ -114,6 +115,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
notificator(0),
rpcConsole(0),
helpMessageDialog(0),
modalOverlay(0),
prevBlocks(0),
spinnerFrame(0),
platformStyle(platformStyle)
@ -241,6 +243,12 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
// Subscribe to notifications from core
subscribeToCoreSignals();
modalOverlay = new ModalOverlay(this->centralWidget());
#ifdef ENABLE_WALLET
if(enableWallet)
connect(walletFrame, SIGNAL(requestedOfSyncWarningInfo()), this, SLOT(showModalOverlay()));
#endif
}
BitcoinGUI::~BitcoinGUI()
@ -491,6 +499,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
// initialize the disable state of the tray icon with the current value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
modalOverlay->setKnownBestHeight(clientModel->getHeaderHeight());
} else {
// Disable possibility to show main window via action
toggleHideAction->setEnabled(false);
@ -705,7 +715,14 @@ void BitcoinGUI::setNumConnections(int count)
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header)
{
if(!clientModel)
if (modalOverlay)
{
if (header)
modalOverlay->setKnownBestHeight(count);
else
modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
}
if (!clientModel)
return;
// Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text)
@ -754,7 +771,10 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
#ifdef ENABLE_WALLET
if(walletFrame)
{
walletFrame->showOutOfSyncWarning(false);
modalOverlay->showHide(true, true);
}
#endif // ENABLE_WALLET
progressBarLabel->setVisible(false);
@ -782,7 +802,10 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
#ifdef ENABLE_WALLET
if(walletFrame)
{
walletFrame->showOutOfSyncWarning(true);
modalOverlay->showHide();
}
#endif // ENABLE_WALLET
tooltip += QString("<br>");
@ -1078,6 +1101,12 @@ void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon)
}
}
void BitcoinGUI::showModalOverlay()
{
if (modalOverlay)
modalOverlay->showHide(false, true);
}
static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style)
{
bool modal = (style & CClientUIInterface::MODAL);

View file

@ -29,6 +29,7 @@ class UnitDisplayStatusBarControl;
class WalletFrame;
class WalletModel;
class HelpMessageDialog;
class ModalOverlay;
class CWallet;
@ -118,6 +119,7 @@ private:
Notificator *notificator;
RPCConsole *rpcConsole;
HelpMessageDialog *helpMessageDialog;
ModalOverlay *modalOverlay;
/** Keep track of previous number of blocks, to detect progress */
int prevBlocks;
@ -229,6 +231,8 @@ private Q_SLOTS:
/** When hideTrayIcon setting is changed in OptionsModel hide or show the icon accordingly. */
void setTrayIconVisible(bool);
void showModalOverlay();
};
class UnitDisplayStatusBarControl : public QLabel

View file

@ -0,0 +1,370 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModalOverlay</class>
<widget class="ModalOverlay" name="ModalOverlay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>385</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="bgWidget" native="true">
<property name="styleSheet">
<string notr="true">#bgWidget { background: rgba(0,0,0,220); }</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutMain" stretch="1">
<property name="leftMargin">
<number>60</number>
</property>
<property name="topMargin">
<number>60</number>
</property>
<property name="rightMargin">
<number>60</number>
</property>
<property name="bottomMargin">
<number>60</number>
</property>
<item>
<widget class="QWidget" name="contentWidget" native="true">
<property name="styleSheet">
<string notr="true">#contentWidget { background: rgba(255,255,255,240); border-radius: 6px; }
QLabel { color: rgb(40,40,40); }</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutSub" stretch="1,0,0,0">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutIconText" stretch="0,1">
<property name="topMargin">
<number>20</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayoutIcon">
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="warningIcon">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/warning</normaloff>
<disabledoff>:/icons/warning</disabledoff>:/icons/warning</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacerWarningIcon">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayoutInfoText">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="infoText">
<property name="text">
<string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoTextStrong">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>This means that recent transactions will not be visible, and the balance will not be up-to-date until this process has completed. Spending bitcoins is not possible during that phase!</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacerInTextSpace">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacerAfterText">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelAmoutOfBlocksLeft">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Amount of blocks left</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="amountOfBlocksLeft">
<property name="text">
<string>unknown...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelLastBlockTime">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Last block time</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="newestBlockDate">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>unknown...</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelSyncDone">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Progress</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayoutSync" stretch="0,1">
<item>
<widget class="QLabel" name="percentageProgress">
<property name="text">
<string>~</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelProgressIncrease">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Progress increase per Hour</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="progressIncreasePerH">
<property name="text">
<string>calculating...</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelEstimatedTimeLeft">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Estimated time left until synced</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="expectedTimeLeft">
<property name="text">
<string>calculating...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutButtons">
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Hide</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ModalOverlay</class>
<extends>QWidget</extends>
<header>modaloverlay.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

150
src/qt/modaloverlay.cpp Normal file
View file

@ -0,0 +1,150 @@
// Copyright (c) 2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "modaloverlay.h"
#include "ui_modaloverlay.h"
#include "guiutil.h"
#include <QResizeEvent>
#include <QPropertyAnimation>
ModalOverlay::ModalOverlay(QWidget *parent) :
QWidget(parent),
ui(new Ui::ModalOverlay),
bestBlockHeight(0),
layerIsVisible(false),
userClosed(false)
{
ui->setupUi(this);
connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(closeClicked()));
if (parent) {
parent->installEventFilter(this);
raise();
}
blockProcessTime.clear();
setVisible(false);
}
ModalOverlay::~ModalOverlay()
{
delete ui;
}
bool ModalOverlay::eventFilter(QObject * obj, QEvent * ev) {
if (obj == parent()) {
if (ev->type() == QEvent::Resize) {
QResizeEvent * rev = static_cast<QResizeEvent*>(ev);
resize(rev->size());
if (!layerIsVisible)
setGeometry(0, height(), width(), height());
}
else if (ev->type() == QEvent::ChildAdded) {
raise();
}
}
return QWidget::eventFilter(obj, ev);
}
//! Tracks parent widget changes
bool ModalOverlay::event(QEvent* ev) {
if (ev->type() == QEvent::ParentAboutToChange) {
if (parent()) parent()->removeEventFilter(this);
}
else if (ev->type() == QEvent::ParentChange) {
if (parent()) {
parent()->installEventFilter(this);
raise();
}
}
return QWidget::event(ev);
}
void ModalOverlay::setKnownBestHeight(int count)
{
if (count > bestBlockHeight)
bestBlockHeight = count;
}
void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress)
{
QDateTime currentDate = QDateTime::currentDateTime();
// keep a vector of samples of verification progress at height
blockProcessTime.push_front(qMakePair(currentDate.currentMSecsSinceEpoch(), nVerificationProgress));
// show progress speed if we have more then one sample
if (blockProcessTime.size() >= 2)
{
// try to get the window from the last 500 seconds or at least 10 samples
double progressStart = blockProcessTime[0].second;
double progressDelta = 0;
double progressPerHour = 0;
qint64 timeDelta = 0;
qint64 remainingMSecs = 0;
double remainingProgress = 1.0 - nVerificationProgress;
for (int i = 1; i < blockProcessTime.size(); i++)
{
QPair<qint64, double> sample = blockProcessTime[i];
// take first sample after 500 seconds or last available one
if (sample.first < (currentDate.currentMSecsSinceEpoch() - 500*1000) || i == blockProcessTime.size()-1)
{
progressDelta = progressStart-sample.second;
timeDelta = blockProcessTime[0].first - sample.first;
progressPerHour = progressDelta/(double)timeDelta*1000*3600;
remainingMSecs = remainingProgress / progressDelta * timeDelta;
break;
}
}
// show progress increase per hour
ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%");
// show expected remaining time
ui->expectedTimeLeft->setText(GUIUtil::formateNiceTimeOffset(remainingMSecs/1000.0));
// keep maximal 5000 samples
static int maxSamples = 5000;
if (blockProcessTime.count() > maxSamples)
blockProcessTime.remove(maxSamples, blockProcessTime.count()-maxSamples);
}
// show the last block date
ui->newestBlockDate->setText(blockDate.toString());
// show the percentage done according to nVerificationProgress
ui->percentageProgress->setText(QString::number(nVerificationProgress*100, 'f', 2)+"%");
ui->progressBar->setValue(nVerificationProgress*100);
// show remaining amount of blocks
if (bestBlockHeight > 0)
ui->amountOfBlocksLeft->setText(QString::number(bestBlockHeight-count));
}
void ModalOverlay::showHide(bool hide, bool userRequested)
{
if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested))
return;
if (!isVisible() && !hide)
setVisible(true);
setGeometry(0, hide ? 0 : height(), width(), height());
QPropertyAnimation* animation = new QPropertyAnimation(this, "pos");
animation->setDuration(300);
animation->setStartValue(QPoint(0, hide ? 0 : this->height()));
animation->setEndValue(QPoint(0, hide ? this->height() : 0));
animation->setEasingCurve(QEasingCurve::OutQuad);
animation->start(QAbstractAnimation::DeleteWhenStopped);
layerIsVisible = !hide;
}
void ModalOverlay::closeClicked()
{
showHide(true);
userClosed = true;
}

44
src/qt/modaloverlay.h Normal file
View file

@ -0,0 +1,44 @@
// Copyright (c) 2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_MODALOVERLAY_H
#define BITCOIN_QT_MODALOVERLAY_H
#include <QDateTime>
#include <QWidget>
namespace Ui {
class ModalOverlay;
}
/** Modal overlay to display information about the chain-sync state */
class ModalOverlay : public QWidget
{
Q_OBJECT
public:
explicit ModalOverlay(QWidget *parent);
~ModalOverlay();
public Q_SLOTS:
void tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress);
void setKnownBestHeight(int count);
// will show or hide the modal layer
void showHide(bool hide = false, bool userRequested = false);
void closeClicked();
protected:
bool eventFilter(QObject * obj, QEvent * ev);
bool event(QEvent* ev);
private:
Ui::ModalOverlay *ui;
int bestBlockHeight; //best known height (based on the headers)
QVector<QPair<qint64, double> > blockProcessTime;
bool layerIsVisible;
bool userClosed;
};
#endif // BITCOIN_QT_MODALOVERLAY_H