2020-04-16 13:14:08 -04:00
// Copyright (c) 2016-2020 The Bitcoin Core developers
2016-07-19 09:51:24 -04:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-09 21:57:53 -03:00
# include <qt/modaloverlay.h>
2017-08-15 12:31:26 -03:00
# include <qt/forms/ui_modaloverlay.h>
2016-07-19 09:51:24 -04:00
2017-11-09 21:57:53 -03:00
# include <chainparams.h>
2020-02-12 17:57:56 -03:00
# include <qt/guiutil.h>
2017-01-19 16:51:59 -03:00
2020-02-12 17:57:56 -03:00
# include <QEasingCurve>
2016-07-19 09:51:24 -04:00
# include <QPropertyAnimation>
2020-02-12 17:57:56 -03:00
# include <QResizeEvent>
2016-07-19 09:51:24 -04:00
2019-10-13 12:31:25 -03:00
ModalOverlay : : ModalOverlay ( bool enable_wallet , QWidget * parent ) :
2016-07-19 09:51:24 -04:00
QWidget ( parent ) ,
ui ( new Ui : : ModalOverlay ) ,
2016-09-26 13:58:51 -03:00
bestHeaderHeight ( 0 ) ,
bestHeaderDate ( QDateTime ( ) ) ,
2016-07-19 09:51:24 -04:00
layerIsVisible ( false ) ,
userClosed ( false )
{
ui - > setupUi ( this ) ;
2018-06-24 11:18:22 -04:00
connect ( ui - > closeButton , & QPushButton : : clicked , this , & ModalOverlay : : closeClicked ) ;
2016-07-19 09:51:24 -04:00
if ( parent ) {
parent - > installEventFilter ( this ) ;
raise ( ) ;
}
blockProcessTime . clear ( ) ;
setVisible ( false ) ;
2019-10-13 12:31:25 -03:00
if ( ! enable_wallet ) {
ui - > infoText - > setVisible ( false ) ;
2020-01-14 15:14:10 -03:00
ui - > infoTextStrong - > setText ( tr ( " %1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain. " ) . arg ( PACKAGE_NAME ) ) ;
2019-10-13 12:31:25 -03:00
}
2020-02-12 17:57:56 -03:00
m_animation . setTargetObject ( this ) ;
m_animation . setPropertyName ( " pos " ) ;
m_animation . setDuration ( 300 /* ms */ ) ;
m_animation . setEasingCurve ( QEasingCurve : : OutQuad ) ;
2016-07-19 09:51:24 -04:00
}
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 ( ) ) ;
2020-02-12 17:57:56 -03:00
if ( m_animation . endValue ( ) . toPoint ( ) . y ( ) > 0 ) {
m_animation . setEndValue ( QPoint ( 0 , height ( ) ) ) ;
}
2016-07-19 09:51:24 -04:00
}
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 ) ;
}
2016-09-13 11:36:24 -03:00
void ModalOverlay : : setKnownBestHeight ( int count , const QDateTime & blockDate )
2016-07-19 09:51:24 -04:00
{
2016-09-26 13:58:51 -03:00
if ( count > bestHeaderHeight ) {
bestHeaderHeight = count ;
bestHeaderDate = blockDate ;
2019-01-03 18:37:18 -03:00
UpdateHeaderSyncLabel ( ) ;
2016-09-13 11:36:24 -03:00
}
2016-07-19 09:51:24 -04:00
}
void ModalOverlay : : tipUpdate ( int count , const QDateTime & blockDate , double nVerificationProgress )
{
QDateTime currentDate = QDateTime : : currentDateTime ( ) ;
// keep a vector of samples of verification progress at height
2016-09-27 15:52:16 -03:00
blockProcessTime . push_front ( qMakePair ( currentDate . toMSecsSinceEpoch ( ) , nVerificationProgress ) ) ;
2016-07-19 09:51:24 -04:00
2018-03-18 11:26:45 -03:00
// show progress speed if we have more than one sample
2017-09-05 19:50:05 -03:00
if ( blockProcessTime . size ( ) > = 2 ) {
2016-07-19 09:51:24 -04:00
double progressDelta = 0 ;
double progressPerHour = 0 ;
qint64 timeDelta = 0 ;
qint64 remainingMSecs = 0 ;
double remainingProgress = 1.0 - nVerificationProgress ;
2017-09-05 19:50:05 -03:00
for ( int i = 1 ; i < blockProcessTime . size ( ) ; i + + ) {
2016-07-19 09:51:24 -04:00
QPair < qint64 , double > sample = blockProcessTime [ i ] ;
// take first sample after 500 seconds or last available one
2016-09-27 15:52:16 -03:00
if ( sample . first < ( currentDate . toMSecsSinceEpoch ( ) - 500 * 1000 ) | | i = = blockProcessTime . size ( ) - 1 ) {
2017-09-05 19:50:05 -03:00
progressDelta = blockProcessTime [ 0 ] . second - sample . second ;
2016-07-19 09:51:24 -04:00
timeDelta = blockProcessTime [ 0 ] . first - sample . first ;
2017-09-05 19:50:05 -03:00
progressPerHour = progressDelta / ( double ) timeDelta * 1000 * 3600 ;
2017-09-05 19:49:36 -03:00
remainingMSecs = ( progressDelta > 0 ) ? remainingProgress / progressDelta * timeDelta : - 1 ;
2016-07-19 09:51:24 -04:00
break ;
}
}
// show progress increase per hour
2017-09-05 19:50:05 -03:00
ui - > progressIncreasePerH - > setText ( QString : : number ( progressPerHour * 100 , ' f ' , 2 ) + " % " ) ;
2016-07-19 09:51:24 -04:00
2017-09-05 19:50:05 -03:00
// show expected remaining time
2018-07-24 11:59:49 -04:00
if ( remainingMSecs > = 0 ) {
2017-09-05 19:49:36 -03:00
ui - > expectedTimeLeft - > setText ( GUIUtil : : formatNiceTimeOffset ( remainingMSecs / 1000.0 ) ) ;
} else {
ui - > expectedTimeLeft - > setText ( QObject : : tr ( " unknown " ) ) ;
}
2016-07-19 09:51:24 -04:00
2016-09-21 05:29:57 -03:00
static const int MAX_SAMPLES = 5000 ;
2017-09-05 19:50:05 -03:00
if ( blockProcessTime . count ( ) > MAX_SAMPLES ) {
blockProcessTime . remove ( MAX_SAMPLES , blockProcessTime . count ( ) - MAX_SAMPLES ) ;
}
2016-07-19 09:51:24 -04:00
}
// 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 ) ;
2016-09-26 13:58:51 -03:00
if ( ! bestHeaderDate . isValid ( ) )
// not syncing
return ;
// estimate the number of headers left based on nPowTargetSpacing
2017-06-19 18:57:31 -04:00
// and check if the gui is not aware of the best header (happens rarely)
2017-01-19 16:51:59 -03:00
int estimateNumHeadersLeft = bestHeaderDate . secsTo ( currentDate ) / Params ( ) . GetConsensus ( ) . nPowTargetSpacing ;
2016-09-26 13:58:51 -03:00
bool hasBestHeader = bestHeaderHeight > = count ;
2016-09-24 14:22:47 -03:00
// show remaining number of blocks
2017-01-19 16:51:59 -03:00
if ( estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC & & hasBestHeader ) {
2016-09-26 13:58:51 -03:00
ui - > numberOfBlocksLeft - > setText ( QString : : number ( bestHeaderHeight - count ) ) ;
} else {
2019-01-03 18:37:18 -03:00
UpdateHeaderSyncLabel ( ) ;
2016-10-20 13:56:03 -03:00
ui - > expectedTimeLeft - > setText ( tr ( " Unknown... " ) ) ;
2016-09-26 13:58:51 -03:00
}
2016-07-19 09:51:24 -04:00
}
2019-01-03 18:37:18 -03:00
void ModalOverlay : : UpdateHeaderSyncLabel ( ) {
int est_headers_left = bestHeaderDate . secsTo ( QDateTime : : currentDateTime ( ) ) / Params ( ) . GetConsensus ( ) . nPowTargetSpacing ;
ui - > numberOfBlocksLeft - > setText ( tr ( " Unknown. Syncing Headers (%1, %2%)... " ) . arg ( bestHeaderHeight ) . arg ( QString : : number ( 100.0 / ( bestHeaderHeight + est_headers_left ) * bestHeaderHeight , ' f ' , 1 ) ) ) ;
}
2016-12-05 05:26:43 -03:00
void ModalOverlay : : toggleVisibility ( )
{
showHide ( layerIsVisible , true ) ;
if ( ! layerIsVisible )
userClosed = true ;
}
2016-07-19 09:51:24 -04:00
void ModalOverlay : : showHide ( bool hide , bool userRequested )
{
if ( ( layerIsVisible & & ! hide ) | | ( ! layerIsVisible & & hide ) | | ( ! hide & & userClosed & & ! userRequested ) )
return ;
2020-07-11 05:59:58 -04:00
Q_EMIT triggered ( hide ) ;
2016-07-19 09:51:24 -04:00
if ( ! isVisible ( ) & & ! hide )
setVisible ( true ) ;
2020-02-12 17:57:56 -03:00
m_animation . setStartValue ( QPoint ( 0 , hide ? 0 : height ( ) ) ) ;
// The eventFilter() updates the endValue if it is required for QEvent::Resize.
m_animation . setEndValue ( QPoint ( 0 , hide ? height ( ) : 0 ) ) ;
m_animation . start ( QAbstractAnimation : : KeepWhenStopped ) ;
2016-07-19 09:51:24 -04:00
layerIsVisible = ! hide ;
}
void ModalOverlay : : closeClicked ( )
{
showHide ( true ) ;
userClosed = true ;
2017-09-05 19:50:05 -03:00
}