splashscreen.cpp raw

   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  }
 240