diff options
author | Jonas Schnelli <dev@jonasschnelli.ch> | 2016-09-23 18:22:45 +0200 |
---|---|---|
committer | Jonas Schnelli <dev@jonasschnelli.ch> | 2016-09-23 18:22:48 +0200 |
commit | 24f72e9f3fd0ce64b944c2a474786f06ed5fa0fd (patch) | |
tree | 234c1cda2d3fc459ba65d6e523b01c68e81586f6 /src | |
parent | d2e46558ba0e11aac4a05c3cf71146c0b6307c8e (diff) | |
parent | 08827df3ecce925928dc3bedcdef63bfca290300 (diff) |
Merge #8371: [Qt] Add out-of-sync modal info layer
08827df [Qt] modalinfolayer: removed unused comments, renamed signal, code style overhaul (Jonas Schnelli)
d8b062e [Qt] only update "amount of blocks left" when the header chain is in-sync (Jonas Schnelli)
e3245b4 [Qt] add out-of-sync modal info layer (Jonas Schnelli)
e47052f [Qt] ClientModel add method to get the height of the header chain (Jonas Schnelli)
a001f18 [Qt] Always pass the numBlocksChanged signal for headers tip changed (Jonas Schnelli)
bd44a04 [Qt] make Out-Of-Sync warning icon clickable (Jonas Schnelli)
0904c3c [Refactor] refactor function that forms human readable text out of a timeoffset (Jonas Schnelli)
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.qt.include | 4 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 59 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 4 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 18 | ||||
-rw-r--r-- | src/qt/clientmodel.h | 3 | ||||
-rw-r--r-- | src/qt/forms/modaloverlay.ui | 373 | ||||
-rw-r--r-- | src/qt/forms/overviewpage.ui | 4 | ||||
-rw-r--r-- | src/qt/guiutil.cpp | 36 | ||||
-rw-r--r-- | src/qt/guiutil.h | 2 | ||||
-rw-r--r-- | src/qt/modaloverlay.cpp | 158 | ||||
-rw-r--r-- | src/qt/modaloverlay.h | 44 | ||||
-rw-r--r-- | src/qt/overviewpage.cpp | 7 | ||||
-rw-r--r-- | src/qt/overviewpage.h | 2 | ||||
-rw-r--r-- | src/qt/walletframe.cpp | 6 | ||||
-rw-r--r-- | src/qt/walletframe.h | 6 | ||||
-rw-r--r-- | src/qt/walletview.cpp | 6 | ||||
-rw-r--r-- | src/qt/walletview.h | 5 |
17 files changed, 708 insertions, 29 deletions
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 8947aeaca0..ea9be82717 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -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 \ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 16d7778a27..dd022ee762 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -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" @@ -115,6 +116,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * notificator(0), rpcConsole(0), helpMessageDialog(0), + modalOverlay(0), prevBlocks(0), spinnerFrame(0), platformStyle(_platformStyle) @@ -239,6 +241,12 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * // Subscribe to notifications from core subscribeToCoreSignals(); + + modalOverlay = new ModalOverlay(this->centralWidget()); +#ifdef ENABLE_WALLET + if(enableWallet) + connect(walletFrame, SIGNAL(requestedSyncWarningInfo()), this, SLOT(showModalOverlay())); +#endif } BitcoinGUI::~BitcoinGUI() @@ -489,6 +497,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->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime())); } else { // Disable possibility to show main window via action toggleHideAction->setEnabled(false); @@ -703,7 +713,17 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header) { - if(!clientModel) + if (modalOverlay) + { + if (header) { + /* use clientmodels getHeaderTipHeight and getHeaderTipTime because the NotifyHeaderTip signal does not fire when updating the best header */ + modalOverlay->setKnownBestHeight(clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime())); + } + 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) @@ -752,7 +772,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); @@ -760,30 +783,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer } else { - // Represent time from last generated block in human readable text - QString timeBehindText; - const int HOUR_IN_SECONDS = 60*60; - const int DAY_IN_SECONDS = 24*60*60; - const int WEEK_IN_SECONDS = 7*24*60*60; - const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar - if(secs < 2*DAY_IN_SECONDS) - { - timeBehindText = tr("%n hour(s)","",secs/HOUR_IN_SECONDS); - } - else if(secs < 2*WEEK_IN_SECONDS) - { - timeBehindText = tr("%n day(s)","",secs/DAY_IN_SECONDS); - } - else if(secs < YEAR_IN_SECONDS) - { - timeBehindText = tr("%n week(s)","",secs/WEEK_IN_SECONDS); - } - else - { - qint64 years = secs / YEAR_IN_SECONDS; - qint64 remainder = secs % YEAR_IN_SECONDS; - timeBehindText = tr("%1 and %2").arg(tr("%n year(s)", "", years)).arg(tr("%n week(s)","", remainder/WEEK_IN_SECONDS)); - } + QString timeBehindText = GUIUtil::formateNiceTimeOffset(secs); progressBarLabel->setVisible(true); progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); @@ -803,7 +803,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>"); @@ -1099,6 +1102,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); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 41770929b4..0eaa44b263 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -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 diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 62a70bacd9..6d19b3d122 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -70,6 +70,22 @@ int ClientModel::getNumBlocks() const return chainActive.Height(); } +int ClientModel::getHeaderTipHeight() const +{ + LOCK(cs_main); + if (!pindexBestHeader) + return 0; + return pindexBestHeader->nHeight; +} + +int64_t ClientModel::getHeaderTipTime() const +{ + LOCK(cs_main); + if (!pindexBestHeader) + return 0; + return pindexBestHeader->GetBlockTime(); +} + quint64 ClientModel::getTotalBytesRecv() const { if(!g_connman) @@ -240,7 +256,7 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification; // if we are in-sync, update the UI regardless of last update time - if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { + if (fHeader || !initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { //pass a async signal to the UI thread QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, Q_ARG(int, pIndex->nHeight), diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 99fd574b9e..3fd8404cbb 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -51,7 +51,8 @@ public: //! Return number of connections, default is in- and outbound (total) int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; int getNumBlocks() const; - + int getHeaderTipHeight() const; + int64_t getHeaderTipTime() const; //! Return number of transactions in the mempool long getMempoolSize() const; //! Return the dynamic memory usage of the mempool diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui new file mode 100644 index 0000000000..ccec1b3e1e --- /dev/null +++ b/src/qt/forms/modaloverlay.ui @@ -0,0 +1,373 @@ +<?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="enabled"> + <bool>false</bool> + </property> + <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. This means that recent transactions will not be visible, and the balance will not be up-to-date until this process has completed.</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>Spending bitcoins may not be 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> diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 6d792d1475..923ed68996 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -61,7 +61,7 @@ <item> <widget class="QPushButton" name="labelWalletStatus"> <property name="enabled"> - <bool>false</bool> + <bool>true</bool> </property> <property name="maximumSize"> <size> @@ -447,7 +447,7 @@ <item> <widget class="QPushButton" name="labelTransactionsStatus"> <property name="enabled"> - <bool>false</bool> + <bool>true</bool> </property> <property name="maximumSize"> <size> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 277a9a1d10..0beaddf997 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -955,4 +955,40 @@ QString formatTimeOffset(int64_t nTimeOffset) return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); } +QString formateNiceTimeOffset(qint64 secs) +{ + // Represent time from last generated block in human readable text + QString timeBehindText; + const int HOUR_IN_SECONDS = 60*60; + const int DAY_IN_SECONDS = 24*60*60; + const int WEEK_IN_SECONDS = 7*24*60*60; + const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar + if(secs < 60) + { + timeBehindText = QObject::tr("%n seconds(s)","",secs); + } + else if(secs < 2*HOUR_IN_SECONDS) + { + timeBehindText = QObject::tr("%n minutes(s)","",secs/60); + } + else if(secs < 2*DAY_IN_SECONDS) + { + timeBehindText = QObject::tr("%n hour(s)","",secs/HOUR_IN_SECONDS); + } + else if(secs < 2*WEEK_IN_SECONDS) + { + timeBehindText = QObject::tr("%n day(s)","",secs/DAY_IN_SECONDS); + } + else if(secs < YEAR_IN_SECONDS) + { + timeBehindText = QObject::tr("%n week(s)","",secs/WEEK_IN_SECONDS); + } + else + { + qint64 years = secs / YEAR_IN_SECONDS; + qint64 remainder = secs % YEAR_IN_SECONDS; + timeBehindText = QObject::tr("%1 and %2").arg(QObject::tr("%n year(s)", "", years)).arg(QObject::tr("%n week(s)","", remainder/WEEK_IN_SECONDS)); + } + return timeBehindText; +} } // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d5a658e7c0..e28f68930f 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -200,6 +200,8 @@ namespace GUIUtil /* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */ QString formatTimeOffset(int64_t nTimeOffset); + QString formateNiceTimeOffset(qint64 secs); + #if defined(Q_OS_MAC) && QT_VERSION >= 0x050000 // workaround for Qt OSX Bug: // https://bugreports.qt-project.org/browse/QTBUG-15631 diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp new file mode 100644 index 0000000000..7b121e9e88 --- /dev/null +++ b/src/qt/modaloverlay.cpp @@ -0,0 +1,158 @@ +// 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, const QDateTime& blockDate) +{ + + /* only update the blockheight if the headerschain-tip is not older then 30 days */ + int64_t now = QDateTime::currentDateTime().toTime_t(); + int64_t btime = blockDate.toTime_t(); + if (btime+3600*24*30 > now) + { + 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) + { + 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 const int MAX_SAMPLES = 5000; + if (blockProcessTime.count() > MAX_SAMPLES) + blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count()-MAX_SAMPLES); + } + + // 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)); + else + ui->expectedTimeLeft->setText(tr("Unknown. Syncing Headers...")); +} + +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; +} diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h new file mode 100644 index 0000000000..bdbe3c39a7 --- /dev/null +++ b/src/qt/modaloverlay.h @@ -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, const QDateTime& blockDate); + + // 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 diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 788d917bcc..7ccdb89c0c 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -140,6 +140,8 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) // start with displaying the "out of sync" warnings showOutOfSyncWarning(true); + connect(ui->labelWalletStatus, SIGNAL(clicked()), this, SLOT(handleOutOfSyncWarningClicks())); + connect(ui->labelTransactionsStatus, SIGNAL(clicked()), this, SLOT(handleOutOfSyncWarningClicks())); } void OverviewPage::handleTransactionClicked(const QModelIndex &index) @@ -148,6 +150,11 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index) Q_EMIT transactionClicked(filter->mapToSource(index)); } +void OverviewPage::handleOutOfSyncWarningClicks() +{ + Q_EMIT outOfSyncWarningClicked(); +} + OverviewPage::~OverviewPage() { delete ui; diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 911443c76a..65cd3341b6 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -42,6 +42,7 @@ public Q_SLOTS: Q_SIGNALS: void transactionClicked(const QModelIndex &index); + void outOfSyncWarningClicked(); private: Ui::OverviewPage *ui; @@ -62,6 +63,7 @@ private Q_SLOTS: void handleTransactionClicked(const QModelIndex &index); void updateAlerts(const QString &warnings); void updateWatchOnlyLabels(bool showWatchOnly); + void handleOutOfSyncWarningClicks(); }; #endif // BITCOIN_QT_OVERVIEWPAGE_H diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 640be4d7a7..69dcc9abb1 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -57,6 +57,8 @@ bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) // Ensure a walletView is able to show the main window connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); + connect(walletView, SIGNAL(outOfSyncWarningClicked()), this, SLOT(outOfSyncWarningClicked())); + return true; } @@ -195,3 +197,7 @@ WalletView *WalletFrame::currentWalletView() return qobject_cast<WalletView*>(walletStack->currentWidget()); } +void WalletFrame::outOfSyncWarningClicked() +{ + Q_EMIT requestedSyncWarningInfo(); +} diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 9a5bc273c2..00c2f56363 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -38,6 +38,10 @@ public: void showOutOfSyncWarning(bool fShow); +Q_SIGNALS: + /** Notify that the user has requested more information about the out-of-sync warning */ + void requestedSyncWarningInfo(); + private: QStackedWidget *walletStack; BitcoinGUI *gui; @@ -78,6 +82,8 @@ public Q_SLOTS: void usedSendingAddresses(); /** Show used receiving addresses */ void usedReceivingAddresses(); + /** Pass on signal over requested out-of-sync-warning information */ + void outOfSyncWarningClicked(); }; #endif // BITCOIN_QT_WALLETFRAME_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 2b61ca1c64..a9518413c2 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -66,6 +66,7 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent): // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); + connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo())); // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); @@ -322,3 +323,8 @@ void WalletView::showProgress(const QString &title, int nProgress) else if (progressDialog) progressDialog->setValue(nProgress); } + +void WalletView::requestedSyncWarningInfo() +{ + Q_EMIT outOfSyncWarningClicked(); +} diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 2045605954..aaa6aacbf0 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -110,6 +110,9 @@ public Q_SLOTS: /** Show progress dialog e.g. for rescan */ void showProgress(const QString &title, int nProgress); + /** User has requested more information about the out of sync state */ + void requestedSyncWarningInfo(); + Q_SIGNALS: /** Signal that we want to show the main window */ void showNormalIfMinimized(); @@ -121,6 +124,8 @@ Q_SIGNALS: void hdEnabledStatusChanged(int hdEnabled); /** Notify that a new transaction appeared */ void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label); + /** Notify that the out of sync warning icon has been pressed */ + void outOfSyncWarningClicked(); }; #endif // BITCOIN_QT_WALLETVIEW_H |