/ src / qt / splashscreen.cpp
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  }