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