Skip to content

Commit 5ef5d19

Browse files
committed
qml: added onboarding logic to allow for custom datadir setting
1 parent b0c1009 commit 5ef5d19

File tree

1 file changed

+206
-90
lines changed

1 file changed

+206
-90
lines changed

src/qml/bitcoin.cpp

Lines changed: 206 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include <QQmlApplicationEngine>
4646
#include <QQmlContext>
4747
#include <QQuickWindow>
48+
#include <QSettings>
4849
#include <QString>
4950
#include <QStyleHints>
5051
#include <QUrl>
@@ -77,7 +78,7 @@ void SetupUIArgs(ArgsManager& argsman)
7778
argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
7879
}
7980

80-
AppMode SetupAppMode()
81+
AppMode* SetupAppMode()
8182
{
8283
bool wallet_enabled;
8384
AppMode::Mode mode;
@@ -93,7 +94,7 @@ AppMode SetupAppMode()
9394
wallet_enabled = false;
9495
#endif // ENABLE_WALLET
9596

96-
return AppMode(mode, wallet_enabled);
97+
return new AppMode(mode, wallet_enabled);
9798
}
9899

99100
bool InitErrorMessageBox(
@@ -103,9 +104,9 @@ bool InitErrorMessageBox(
103104
{
104105
QQmlApplicationEngine engine;
105106

106-
AppMode app_mode = SetupAppMode();
107+
AppMode* app_mode = SetupAppMode();
107108

108-
qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode);
109+
qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", app_mode);
109110
engine.rootContext()->setContextProperty("message", QString::fromStdString(message.translated));
110111
engine.load(QUrl(QStringLiteral("qrc:///qml/pages/initerrormessage.qml")));
111112
if (engine.rootObjects().isEmpty()) {
@@ -158,6 +159,193 @@ void setupChainQSettings(QGuiApplication* app, QString chain)
158159
app->setApplicationName(QAPP_APP_NAME_REGTEST);
159160
}
160161
}
162+
163+
// added this function to set custom data directory when starting node
164+
bool setCustomDataDir(QString strDataDir)
165+
{
166+
if(fs::exists(GUIUtil::QStringToPath(strDataDir))){
167+
gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::QStringToPath(strDataDir)));
168+
gArgs.ClearPathCache();
169+
return true;
170+
} else {
171+
return false;
172+
}
173+
}
174+
175+
QGuiApplication* m_app;
176+
QQmlApplicationEngine* m_engine;
177+
// ArgsManager* m_gArgs;
178+
QQuickWindow* m_window;
179+
boost::signals2::connection m_handler_message_box;
180+
std::unique_ptr<interfaces::Init> m_init;
181+
std::unique_ptr<interfaces::Node> m_node;
182+
std::unique_ptr<interfaces::Chain> m_chain;
183+
NodeModel* m_node_model{nullptr};
184+
InitExecutor* m_executor{nullptr};
185+
ChainModel* m_chain_model{nullptr};
186+
OptionsQmlModel* m_options_model{nullptr};
187+
// OnboardingModel* m_onboarding_model{nullptr};
188+
int m_argc;
189+
char** m_argv;
190+
bool m_prune;
191+
int m_prune_size_gb;
192+
NetworkTrafficTower* m_network_traffic_tower;
193+
PeerTableModel* m_peer_model;
194+
PeerListSortProxy* m_peer_model_sort_proxy;
195+
QThread* m_introThread;
196+
bool m_isOnboarded;
197+
198+
bool createNode(QGuiApplication& app, QQmlApplicationEngine& engine, int& argc, char* argv[], ArgsManager& gArgs)
199+
{
200+
m_engine = &engine;
201+
202+
InitLogging(gArgs);
203+
InitParameterInteraction(gArgs);
204+
205+
m_init = interfaces::MakeGuiInit(argc, argv);
206+
207+
m_node = m_init->makeNode();
208+
m_chain = m_init->makeChain();
209+
210+
if (!m_node->baseInitialize()) {
211+
// A dialog with detailed error will have been shown by InitError().
212+
return EXIT_FAILURE;
213+
}
214+
215+
m_handler_message_box.disconnect();
216+
217+
m_node_model = new NodeModel{*m_node};
218+
m_executor = new InitExecutor{*m_node};
219+
QObject::connect(m_node_model, &NodeModel::requestedInitialize, m_executor, &InitExecutor::initialize);
220+
QObject::connect(m_node_model, &NodeModel::requestedShutdown, m_executor, &InitExecutor::shutdown);
221+
QObject::connect(m_executor, &InitExecutor::initializeResult, m_node_model, &NodeModel::initializeResult);
222+
QObject::connect(m_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection);
223+
224+
m_network_traffic_tower = new NetworkTrafficTower{*m_node_model};
225+
#ifdef __ANDROID__
226+
AndroidNotifier android_notifier{*m_node_model};
227+
#endif
228+
229+
m_chain_model = new ChainModel{*m_chain};
230+
m_chain_model->setCurrentNetworkName(QString::fromStdString(ChainTypeToString(gArgs.GetChainType())));
231+
setupChainQSettings(m_app, m_chain_model->currentNetworkName());
232+
233+
QObject::connect(m_node_model, &NodeModel::setTimeRatioList, m_chain_model, &ChainModel::setTimeRatioList);
234+
QObject::connect(m_node_model, &NodeModel::setTimeRatioListInitial, m_chain_model, &ChainModel::setTimeRatioListInitial);
235+
236+
qGuiApp->setQuitOnLastWindowClosed(false);
237+
QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] {
238+
m_node->startShutdown();
239+
});
240+
241+
m_peer_model = new PeerTableModel{*m_node, nullptr};
242+
m_peer_model_sort_proxy = new PeerListSortProxy{nullptr};
243+
m_peer_model_sort_proxy->setSourceModel(m_peer_model);
244+
245+
m_engine->rootContext()->setContextProperty("networkTrafficTower", m_network_traffic_tower);
246+
m_engine->rootContext()->setContextProperty("nodeModel", m_node_model);
247+
m_engine->rootContext()->setContextProperty("chainModel", m_chain_model);
248+
m_engine->rootContext()->setContextProperty("peerTableModel", m_peer_model);
249+
m_engine->rootContext()->setContextProperty("peerListModelProxy", m_peer_model_sort_proxy);
250+
251+
m_options_model->setNode(&(*m_node), m_isOnboarded);
252+
253+
QObject::connect(m_options_model, &OptionsQmlModel::requestedShutdown, m_executor, &InitExecutor::shutdown);
254+
255+
m_engine->rootContext()->setContextProperty("optionsModel", m_options_model);
256+
257+
m_node_model->startShutdownPolling();
258+
259+
return true;
260+
}
261+
262+
void startNodeAndTransitionSlot() { createNode(*m_app, *m_engine, m_argc, m_argv, gArgs); }
263+
264+
bool startNode(QGuiApplication& app, QQmlApplicationEngine& engine, int& argc, char* argv[])
265+
{
266+
m_engine = &engine;
267+
QScopedPointer<const NetworkStyle> network_style{NetworkStyle::instantiate(Params().GetChainType())};
268+
assert(!network_style.isNull());
269+
m_engine->addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});
270+
271+
m_isOnboarded = true;
272+
273+
m_options_model = new OptionsQmlModel{nullptr, m_isOnboarded};
274+
m_engine->rootContext()->setContextProperty("optionsModel", m_options_model);
275+
276+
// moved this so that the settings.json file is read and parsed before creating the node
277+
std::string error;
278+
/// Read and parse settings.json file.
279+
std::vector<std::string> errors;
280+
if (!gArgs.ReadSettingsFile(&errors)) {
281+
error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors));
282+
InitError(Untranslated(error));
283+
return EXIT_FAILURE;
284+
}
285+
286+
createNode(*m_app, *m_engine, argc, argv, gArgs);
287+
288+
AppMode* app_mode = SetupAppMode();
289+
290+
qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", app_mode);
291+
qmlRegisterType<BlockClockDial>("org.bitcoincore.qt", 1, 0, "BlockClockDial");
292+
qmlRegisterType<LineGraph>("org.bitcoincore.qt", 1, 0, "LineGraph");
293+
294+
m_engine->load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));
295+
if (m_engine->rootObjects().isEmpty()) {
296+
return EXIT_FAILURE;
297+
}
298+
299+
auto window = qobject_cast<QQuickWindow*>(engine.rootObjects().first());
300+
if (!window) {
301+
return EXIT_FAILURE;
302+
}
303+
304+
// Install qDebug() message handler to route to debug.log
305+
qInstallMessageHandler(DebugMessageHandler);
306+
307+
qInfo() << "Graphics API in use:" << QmlUtil::GraphicsApi(window);
308+
309+
return qGuiApp->exec();
310+
}
311+
312+
bool startOnboarding(QGuiApplication& app, QQmlApplicationEngine& engine, ArgsManager& gArgs)
313+
{
314+
m_engine = &engine;
315+
QScopedPointer<const NetworkStyle> network_style{NetworkStyle::instantiate(Params().GetChainType())};
316+
assert(!network_style.isNull());
317+
m_engine->addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});
318+
319+
m_isOnboarded = false;
320+
321+
m_options_model = new OptionsQmlModel{nullptr, m_isOnboarded};
322+
323+
if (gArgs.IsArgSet("-resetguisettings")) {
324+
m_options_model->defaultReset();
325+
}
326+
327+
m_engine->rootContext()->setContextProperty("optionsModel", m_options_model);
328+
329+
QObject::connect(m_options_model, &OptionsQmlModel::onboardingFinished, startNodeAndTransitionSlot);
330+
331+
AppMode* app_mode = SetupAppMode();
332+
333+
qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", app_mode);
334+
qmlRegisterType<BlockClockDial>("org.bitcoincore.qt", 1, 0, "BlockClockDial");
335+
qmlRegisterType<LineGraph>("org.bitcoincore.qt", 1, 0, "LineGraph");
336+
337+
m_engine->load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));
338+
if (m_engine->rootObjects().isEmpty()) {
339+
return EXIT_FAILURE;
340+
}
341+
342+
auto window = qobject_cast<QQuickWindow*>(m_engine->rootObjects().first());
343+
if (!window) {
344+
return EXIT_FAILURE;
345+
}
346+
347+
return qGuiApp->exec();
348+
}
161349
} // namespace
162350

163351

@@ -175,9 +363,7 @@ int QmlGuiMain(int argc, char* argv[])
175363
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls);
176364
QGuiApplication app(argc, argv);
177365

178-
auto handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(InitErrorMessageBox);
179-
180-
std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
366+
auto m_handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(InitErrorMessageBox);
181367

182368
SetupEnvironment();
183369
util::ThreadSetInternalName("main");
@@ -189,6 +375,10 @@ int QmlGuiMain(int argc, char* argv[])
189375
app.setOrganizationDomain(QAPP_ORG_DOMAIN);
190376
app.setApplicationName(QAPP_APP_NAME_DEFAULT);
191377

378+
QSettings settings;
379+
QString dataDir;
380+
dataDir = settings.value("strDataDir", dataDir).toString();
381+
192382
/// Parse command-line options. We do this after qt in order to show an error if there are problems parsing these.
193383
SetupServerArgs(gArgs);
194384
SetupUIArgs(gArgs);
@@ -218,16 +408,9 @@ int QmlGuiMain(int argc, char* argv[])
218408
return EXIT_FAILURE;
219409
}
220410

221-
/// Read and parse settings.json file.
222-
std::vector<std::string> errors;
223-
if (!gArgs.ReadSettingsFile(&errors)) {
224-
error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors));
225-
InitError(Untranslated(error));
226-
return EXIT_FAILURE;
227-
}
228-
229411
QVariant need_onboarding(true);
230-
if (gArgs.IsArgSet("-datadir") && !gArgs.GetPathArg("-datadir").empty()) {
412+
if ((gArgs.IsArgSet("-datadir") && !gArgs.GetPathArg("-datadir").empty()) || fs::exists(GUIUtil::QStringToPath(dataDir)) ) {
413+
setCustomDataDir(dataDir);
231414
need_onboarding.setValue(false);
232415
} else if (ConfigurationFileExists(gArgs)) {
233416
need_onboarding.setValue(false);
@@ -240,89 +423,22 @@ int QmlGuiMain(int argc, char* argv[])
240423
// Default printtoconsole to false for the GUI. GUI programs should not
241424
// print to the console unnecessarily.
242425
gArgs.SoftSetBoolArg("-printtoconsole", false);
243-
InitLogging(gArgs);
244-
InitParameterInteraction(gArgs);
245426

246427
GUIUtil::LogQtInfo();
247-
248-
std::unique_ptr<interfaces::Node> node = init->makeNode();
249-
std::unique_ptr<interfaces::Chain> chain = init->makeChain();
250-
if (!node->baseInitialize()) {
251-
// A dialog with detailed error will have been shown by InitError().
252-
return EXIT_FAILURE;
253-
}
254-
255-
handler_message_box.disconnect();
256-
257-
NodeModel node_model{*node};
258-
InitExecutor init_executor{*node};
259-
QObject::connect(&node_model, &NodeModel::requestedInitialize, &init_executor, &InitExecutor::initialize);
260-
QObject::connect(&node_model, &NodeModel::requestedShutdown, &init_executor, &InitExecutor::shutdown);
261-
QObject::connect(&init_executor, &InitExecutor::initializeResult, &node_model, &NodeModel::initializeResult);
262-
QObject::connect(&init_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection);
263-
// QObject::connect(&init_executor, &InitExecutor::runawayException, &node_model, &NodeModel::handleRunawayException);
264-
265-
NetworkTrafficTower network_traffic_tower{node_model};
266-
#ifdef __ANDROID__
267-
AndroidNotifier android_notifier{node_model};
268-
#endif
269-
270-
ChainModel chain_model{*chain};
271-
chain_model.setCurrentNetworkName(QString::fromStdString(ChainTypeToString(gArgs.GetChainType())));
272-
setupChainQSettings(&app, chain_model.currentNetworkName());
273-
274-
QObject::connect(&node_model, &NodeModel::setTimeRatioList, &chain_model, &ChainModel::setTimeRatioList);
275-
QObject::connect(&node_model, &NodeModel::setTimeRatioListInitial, &chain_model, &ChainModel::setTimeRatioListInitial);
276-
277-
qGuiApp->setQuitOnLastWindowClosed(false);
278-
QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] {
279-
node->startShutdown();
280-
});
281-
282-
PeerTableModel peer_model{*node, nullptr};
283-
PeerListSortProxy peer_model_sort_proxy{nullptr};
284-
peer_model_sort_proxy.setSourceModel(&peer_model);
285-
286428
GUIUtil::LoadFont(":/fonts/inter/regular");
287429
GUIUtil::LoadFont(":/fonts/inter/semibold");
288430

289-
QQmlApplicationEngine engine;
431+
m_app = &app;
290432

291-
QScopedPointer<const NetworkStyle> network_style{NetworkStyle::instantiate(Params().GetChainType())};
292-
assert(!network_style.isNull());
293-
engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});
294-
295-
engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower);
296-
engine.rootContext()->setContextProperty("nodeModel", &node_model);
297-
engine.rootContext()->setContextProperty("chainModel", &chain_model);
298-
engine.rootContext()->setContextProperty("peerTableModel", &peer_model);
299-
engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy);
433+
QQmlApplicationEngine engine;
300434

301-
OptionsQmlModel options_model(*node, !need_onboarding.toBool());
302-
engine.rootContext()->setContextProperty("optionsModel", &options_model);
303435
engine.rootContext()->setContextProperty("needOnboarding", need_onboarding);
304436

305-
AppMode app_mode = SetupAppMode();
306-
307-
qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode);
308-
qmlRegisterType<BlockClockDial>("org.bitcoincore.qt", 1, 0, "BlockClockDial");
309-
qmlRegisterType<LineGraph>("org.bitcoincore.qt", 1, 0, "LineGraph");
310-
311-
engine.load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));
312-
if (engine.rootObjects().isEmpty()) {
313-
return EXIT_FAILURE;
314-
}
315-
316-
auto window = qobject_cast<QQuickWindow*>(engine.rootObjects().first());
317-
if (!window) {
318-
return EXIT_FAILURE;
437+
if(need_onboarding.toBool()) {
438+
startOnboarding(*m_app, engine, gArgs);
439+
} else {
440+
startNode(*m_app, engine, argc, argv);
319441
}
320442

321-
// Install qDebug() message handler to route to debug.log
322-
qInstallMessageHandler(DebugMessageHandler);
323-
324-
qInfo() << "Graphics API in use:" << QmlUtil::GraphicsApi(window);
325-
326-
node_model.startShutdownPolling();
327-
return qGuiApp->exec();
443+
return 0;
328444
}

0 commit comments

Comments
 (0)