2024-03-15 18:16:03 +01:00
// Copyright (c) 2016-2019 The Palladium Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <qt/modaloverlay.h>
# include <qt/forms/ui_modaloverlay.h>
# include <qt/guiutil.h>
# include <chainparams.h>
# include <QResizeEvent>
# include <QPropertyAnimation>
ModalOverlay : : ModalOverlay ( bool enable_wallet , QWidget * parent ) :
QWidget ( parent ) ,
ui ( new Ui : : ModalOverlay ) ,
bestHeaderHeight ( 0 ) ,
bestHeaderDate ( QDateTime ( ) ) ,
layerIsVisible ( false ) ,
userClosed ( false )
{
ui - > setupUi ( this ) ;
connect ( ui - > closeButton , & QPushButton : : clicked , this , & ModalOverlay : : closeClicked ) ;
if ( parent ) {
parent - > installEventFilter ( this ) ;
raise ( ) ;
}
2026-01-26 17:45:48 +01:00
// Set semi-transparent background
2026-01-11 18:51:44 +01:00
QPalette pal = ui - > bgWidget - > palette ( ) ;
2026-01-26 17:45:48 +01:00
QColor bgColor = pal . color ( QPalette : : Window ) ;
bgColor . setAlpha ( 230 ) ; // Semi-transparent (0-255, where 255 is opaque)
pal . setColor ( QPalette : : Window , bgColor ) ;
2026-01-11 18:51:44 +01:00
ui - > bgWidget - > setAutoFillBackground ( true ) ;
ui - > bgWidget - > setPalette ( pal ) ;
2024-03-15 18:16:03 +01:00
blockProcessTime . clear ( ) ;
setVisible ( false ) ;
if ( ! enable_wallet ) {
ui - > infoText - > setVisible ( false ) ;
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 ) ) ;
}
}
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 )
{
if ( count > bestHeaderHeight ) {
bestHeaderHeight = count ;
bestHeaderDate = blockDate ;
UpdateHeaderSyncLabel ( ) ;
}
}
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 . toMSecsSinceEpoch ( ) , nVerificationProgress ) ) ;
// show progress speed if we have more than one sample
if ( blockProcessTime . size ( ) > = 2 ) {
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 . toMSecsSinceEpoch ( ) - 500 * 1000 ) | | i = = blockProcessTime . size ( ) - 1 ) {
progressDelta = blockProcessTime [ 0 ] . second - sample . second ;
timeDelta = blockProcessTime [ 0 ] . first - sample . first ;
progressPerHour = progressDelta / ( double ) timeDelta * 1000 * 3600 ;
remainingMSecs = ( progressDelta > 0 ) ? remainingProgress / progressDelta * timeDelta : - 1 ;
break ;
}
}
// show progress increase per hour
ui - > progressIncreasePerH - > setText ( QString : : number ( progressPerHour * 100 , ' f ' , 2 ) + " % " ) ;
// show expected remaining time
if ( remainingMSecs > = 0 ) {
ui - > expectedTimeLeft - > setText ( GUIUtil : : formatNiceTimeOffset ( remainingMSecs / 1000.0 ) ) ;
} else {
ui - > expectedTimeLeft - > setText ( QObject : : tr ( " unknown " ) ) ;
}
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 ) ;
if ( ! bestHeaderDate . isValid ( ) )
// not syncing
return ;
2025-11-17 17:43:13 +01:00
// estimate the number of headers left based on nPowTargetSpacingV2
2024-03-15 18:16:03 +01:00
// and check if the gui is not aware of the best header (happens rarely)
2025-11-17 17:43:13 +01:00
int estimateNumHeadersLeft = bestHeaderDate . secsTo ( currentDate ) / Params ( ) . GetConsensus ( ) . nPowTargetSpacingV2 ;
2024-03-15 18:16:03 +01:00
bool hasBestHeader = bestHeaderHeight > = count ;
// show remaining number of blocks
if ( estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC & & hasBestHeader ) {
ui - > numberOfBlocksLeft - > setText ( QString : : number ( bestHeaderHeight - count ) ) ;
} else {
UpdateHeaderSyncLabel ( ) ;
ui - > expectedTimeLeft - > setText ( tr ( " Unknown... " ) ) ;
}
}
void ModalOverlay : : UpdateHeaderSyncLabel ( ) {
2025-11-17 17:43:13 +01:00
int est_headers_left = bestHeaderDate . secsTo ( QDateTime : : currentDateTime ( ) ) / Params ( ) . GetConsensus ( ) . nPowTargetSpacingV2 ;
2024-03-15 18:16:03 +01:00
ui - > numberOfBlocksLeft - > setText ( tr ( " Unknown. Syncing Headers (%1, %2%)... " ) . arg ( bestHeaderHeight ) . arg ( QString : : number ( 100.0 / ( bestHeaderHeight + est_headers_left ) * bestHeaderHeight , ' f ' , 1 ) ) ) ;
}
void ModalOverlay : : toggleVisibility ( )
{
showHide ( layerIsVisible , true ) ;
if ( ! layerIsVisible )
userClosed = true ;
}
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 ;
}