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