splashscreen.cpp
1 // Copyright (c) 2011-2022 The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #if defined(HAVE_CONFIG_H) 6 #include <config/bitcoin-config.h> 7 #endif 8 9 #include <qt/splashscreen.h> 10 11 #include <clientversion.h> 12 #include <common/system.h> 13 #include <interfaces/handler.h> 14 #include <interfaces/node.h> 15 #include <interfaces/wallet.h> 16 #include <qt/guiutil.h> 17 #include <qt/networkstyle.h> 18 #include <qt/walletmodel.h> 19 #include <util/translation.h> 20 21 #include <functional> 22 23 #include <QApplication> 24 #include <QCloseEvent> 25 #include <QPainter> 26 #include <QRadialGradient> 27 #include <QScreen> 28 29 30 SplashScreen::SplashScreen(const NetworkStyle* networkStyle) 31 : QWidget() 32 { 33 // set reference point, paddings 34 int paddingRight = 50; 35 int paddingTop = 50; 36 int titleVersionVSpace = 17; 37 int titleCopyrightVSpace = 40; 38 39 float fontFactor = 1.0; 40 float devicePixelRatio = 1.0; 41 devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio(); 42 43 // define text to place 44 QString titleText = PACKAGE_NAME; 45 QString versionText = QString("Version %1").arg(QString::fromStdString(FormatFullVersion())); 46 QString copyrightText = QString::fromUtf8(CopyrightHolders(strprintf("\xc2\xA9 %u-%u ", 2009, COPYRIGHT_YEAR)).c_str()); 47 const QString& titleAddText = networkStyle->getTitleAddText(); 48 49 QString font = QApplication::font().toString(); 50 51 // create a bitmap according to device pixelratio 52 QSize splashSize(480*devicePixelRatio,320*devicePixelRatio); 53 pixmap = QPixmap(splashSize); 54 55 // change to HiDPI if it makes sense 56 pixmap.setDevicePixelRatio(devicePixelRatio); 57 58 QPainter pixPaint(&pixmap); 59 pixPaint.setPen(QColor(100,100,100)); 60 61 // draw a slightly radial gradient 62 QRadialGradient gradient(QPoint(0,0), splashSize.width()/devicePixelRatio); 63 gradient.setColorAt(0, Qt::white); 64 gradient.setColorAt(1, QColor(247,247,247)); 65 QRect rGradient(QPoint(0,0), splashSize); 66 pixPaint.fillRect(rGradient, gradient); 67 68 // draw the bitcoin icon, expected size of PNG: 1024x1024 69 QRect rectIcon(QPoint(-150,-122), QSize(430,430)); 70 71 const QSize requiredSize(1024,1024); 72 QPixmap icon(networkStyle->getAppIcon().pixmap(requiredSize)); 73 74 pixPaint.drawPixmap(rectIcon, icon); 75 76 // check font size and drawing with 77 pixPaint.setFont(QFont(font, 33*fontFactor)); 78 QFontMetrics fm = pixPaint.fontMetrics(); 79 int titleTextWidth = GUIUtil::TextWidth(fm, titleText); 80 if (titleTextWidth > 176) { 81 fontFactor = fontFactor * 176 / titleTextWidth; 82 } 83 84 pixPaint.setFont(QFont(font, 33*fontFactor)); 85 fm = pixPaint.fontMetrics(); 86 titleTextWidth = GUIUtil::TextWidth(fm, titleText); 87 pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight,paddingTop,titleText); 88 89 pixPaint.setFont(QFont(font, 15*fontFactor)); 90 91 // if the version string is too long, reduce size 92 fm = pixPaint.fontMetrics(); 93 int versionTextWidth = GUIUtil::TextWidth(fm, versionText); 94 if(versionTextWidth > titleTextWidth+paddingRight-10) { 95 pixPaint.setFont(QFont(font, 10*fontFactor)); 96 titleVersionVSpace -= 5; 97 } 98 pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight+2,paddingTop+titleVersionVSpace,versionText); 99 100 // draw copyright stuff 101 { 102 pixPaint.setFont(QFont(font, 10*fontFactor)); 103 const int x = pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight; 104 const int y = paddingTop+titleCopyrightVSpace; 105 QRect copyrightRect(x, y, pixmap.width() - x - paddingRight, pixmap.height() - y); 106 pixPaint.drawText(copyrightRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, copyrightText); 107 } 108 109 // draw additional text if special network 110 if(!titleAddText.isEmpty()) { 111 QFont boldFont = QFont(font, 10*fontFactor); 112 boldFont.setWeight(QFont::Bold); 113 pixPaint.setFont(boldFont); 114 fm = pixPaint.fontMetrics(); 115 int titleAddTextWidth = GUIUtil::TextWidth(fm, titleAddText); 116 pixPaint.drawText(pixmap.width()/devicePixelRatio-titleAddTextWidth-10,15,titleAddText); 117 } 118 119 pixPaint.end(); 120 121 // Set window title 122 setWindowTitle(titleText + " " + titleAddText); 123 124 // Resize window and move to center of desktop, disallow resizing 125 QRect r(QPoint(), QSize(pixmap.size().width()/devicePixelRatio,pixmap.size().height()/devicePixelRatio)); 126 resize(r.size()); 127 setFixedSize(r.size()); 128 move(QGuiApplication::primaryScreen()->geometry().center() - r.center()); 129 130 installEventFilter(this); 131 132 GUIUtil::handleCloseWindowShortcut(this); 133 } 134 135 SplashScreen::~SplashScreen() 136 { 137 if (m_node) unsubscribeFromCoreSignals(); 138 } 139 140 void SplashScreen::setNode(interfaces::Node& node) 141 { 142 assert(!m_node); 143 m_node = &node; 144 subscribeToCoreSignals(); 145 if (m_shutdown) m_node->startShutdown(); 146 } 147 148 void SplashScreen::shutdown() 149 { 150 m_shutdown = true; 151 if (m_node) m_node->startShutdown(); 152 } 153 154 bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) { 155 if (ev->type() == QEvent::KeyPress) { 156 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev); 157 if (keyEvent->key() == Qt::Key_Q) { 158 shutdown(); 159 } 160 } 161 return QObject::eventFilter(obj, ev); 162 } 163 164 static void InitMessage(SplashScreen *splash, const std::string &message) 165 { 166 bool invoked = QMetaObject::invokeMethod(splash, "showMessage", 167 Qt::QueuedConnection, 168 Q_ARG(QString, QString::fromStdString(message)), 169 Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter), 170 Q_ARG(QColor, QColor(55,55,55))); 171 assert(invoked); 172 } 173 174 static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible) 175 { 176 InitMessage(splash, title + std::string("\n") + 177 (resume_possible ? SplashScreen::tr("(press q to shutdown and continue later)").toStdString() 178 : SplashScreen::tr("press q to shutdown").toStdString()) + 179 strprintf("\n%d", nProgress) + "%"); 180 } 181 182 void SplashScreen::subscribeToCoreSignals() 183 { 184 // Connect signals to client 185 m_handler_init_message = m_node->handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1)); 186 m_handler_show_progress = m_node->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); 187 m_handler_init_wallet = m_node->handleInitWallet([this]() { handleLoadWallet(); }); 188 } 189 190 void SplashScreen::handleLoadWallet() 191 { 192 #ifdef ENABLE_WALLET 193 if (!WalletModel::isWalletEnabled()) return; 194 m_handler_load_wallet = m_node->walletLoader().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { 195 m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false))); 196 m_connected_wallets.emplace_back(std::move(wallet)); 197 }); 198 #endif 199 } 200 201 void SplashScreen::unsubscribeFromCoreSignals() 202 { 203 // Disconnect signals from client 204 m_handler_init_message->disconnect(); 205 m_handler_show_progress->disconnect(); 206 for (const auto& handler : m_connected_wallet_handlers) { 207 handler->disconnect(); 208 } 209 m_connected_wallet_handlers.clear(); 210 m_connected_wallets.clear(); 211 } 212 213 void SplashScreen::showMessage(const QString &message, int alignment, const QColor &color) 214 { 215 curMessage = message; 216 curAlignment = alignment; 217 curColor = color; 218 update(); 219 } 220 221 void SplashScreen::paintEvent(QPaintEvent *event) 222 { 223 QPainter painter(this); 224 painter.drawPixmap(0, 0, pixmap); 225 QRect r = rect().adjusted(5, 5, -5, -5); 226 painter.setPen(curColor); 227 painter.drawText(r, curAlignment, curMessage); 228 } 229 230 void SplashScreen::closeEvent(QCloseEvent *event) 231 { 232 shutdown(); // allows an "emergency" shutdown during startup 233 event->ignore(); 234 }