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