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