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