From 4843b81bbb48718dc532dfdc0aaf4a3e235b153a Mon Sep 17 00:00:00 2001 From: Jarol Rodriguez Date: Wed, 14 Dec 2022 00:38:27 -0500 Subject: [PATCH 01/17] qml: introduce check icon --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/imageprovider.cpp | 5 +++++ src/qml/res/icons/check.png | Bin 0 -> 829 bytes src/qml/res/src/check.svg | 3 +++ 5 files changed, 10 insertions(+) create mode 100644 src/qml/res/icons/check.png create mode 100644 src/qml/res/src/check.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index f65fd164a0..8f7e560357 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -305,6 +305,7 @@ QML_RES_ICONS = \ qml/res/icons/blocktime-light.png \ qml/res/icons/caret-left.png \ qml/res/icons/caret-right.png \ + qml/res/icons/check.png \ qml/res/icons/cross.png \ qml/res/icons/export.png \ qml/res/icons/gear.png \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 86a5afe6da..d1d1a84b1f 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -48,6 +48,7 @@ ../qt/res/icons/bitcoin.png res/icons/caret-left.png res/icons/caret-right.png + res/icons/check.png res/icons/cross.png res/icons/export.png res/icons/gear.png diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp index 7144569447..b93c036f6e 100644 --- a/src/qml/imageprovider.cpp +++ b/src/qml/imageprovider.cpp @@ -62,6 +62,11 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize return QIcon(":/icons/caret-right").pixmap(requested_size); } + if (id == "check") { + *size = requested_size; + return QIcon(":/icons/check").pixmap(requested_size); + } + if (id == "cross") { *size = requested_size; return QIcon(":/icons/cross").pixmap(requested_size); diff --git a/src/qml/res/icons/check.png b/src/qml/res/icons/check.png new file mode 100644 index 0000000000000000000000000000000000000000..ef408cb502ef89a400317e3802da4cfcab866689 GIT binary patch literal 829 zcmV-D1H$}?P)nnL8h~CWL0GPg>u};=0jxK21gD?rdJq?8xzxjB6Tr8j@*b$m*L2g$W+G! zxRJq|$g~HJ?2b(Dkck^rR3p=JIC3d6-Gn1^(}^NAF*$CGL2V3$Zm5P3Z5MS&6}$Ct2I?!)B*h zo52~&^T>11@JaY=KOlkxOjl(2h)^)19LhuJwVJ& z&UYx#!SvruwA<~M>50AR_!6NZ*liy`rjtm7$@mBbI+wBZ-MYy1J;fx%_`HPzT}1|c zlN;E6Hzq-bZF(gU}&!Iqfkwb5$wA(M8%tPW7@CXWYAGsLDoyc?^ ziIm3!D9{rW(!FKVwEbprD+=@y<%DJNJ~C}jf@N?e3iK+KgzTTb?ROlPpg`|25!U$B zqDWmVO5%;-bQI`QrX=cGWV(u!I1&Z=f@%2ALh0Uq_d|icV-^5l9IqqO-lWPtbRlfN zFb@DbBh!aulQP@UiLh0(0A>7)Oe>H=ThM{9)$tHA-9}2SOB?F7W~LyuR-zg8(&41q z;?yI$?@RJYxrM1k-L@tU%*D?r*uyw6Bi|xy?aYE3)A1EDos27!^D%b&b!L+PO(5;8 zghM5q!*|s4D2tMKzqJPkBme*a0D}I%ZbzVg0000000001Lqwfk1B&Lo00000NkvXX Hu0mjfngD)} literal 0 HcmV?d00001 diff --git a/src/qml/res/src/check.svg b/src/qml/res/src/check.svg new file mode 100644 index 0000000000..a393deb9b4 --- /dev/null +++ b/src/qml/res/src/check.svg @@ -0,0 +1,3 @@ + + + From a99db317ef0d8b7a34acb59d9a116f8fea40fc7e Mon Sep 17 00:00:00 2001 From: Jarol Rodriguez Date: Wed, 14 Dec 2022 01:08:15 -0500 Subject: [PATCH 02/17] qml: add check mark to OptionButton control This does away with a loadable detail and places a check mark icon in it's place. --- src/qml/components/ConnectionOptions.qml | 8 -------- src/qml/components/StorageOptions.qml | 8 -------- src/qml/controls/OptionButton.qml | 11 +++++++++-- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/qml/components/ConnectionOptions.qml b/src/qml/components/ConnectionOptions.qml index be016fe8dc..e0c4079d7e 100644 --- a/src/qml/components/ConnectionOptions.qml +++ b/src/qml/components/ConnectionOptions.qml @@ -31,13 +31,5 @@ ColumnLayout { ButtonGroup.group: group text: qsTr("Only when on Wi-Fi") description: qsTr("Loads quickly when on wi-fi and pauses when on cellular data.") - detail: ProgressIndicator { - implicitWidth: 50 - SequentialAnimation on progress { - loops: Animation.Infinite - SmoothedAnimation { to: 1; velocity: 1; } - SmoothedAnimation { to: 0; velocity: 1; } - } - } } } diff --git a/src/qml/components/StorageOptions.qml b/src/qml/components/StorageOptions.qml index f25a892368..9c30e0f0de 100644 --- a/src/qml/components/StorageOptions.qml +++ b/src/qml/components/StorageOptions.qml @@ -19,19 +19,11 @@ ColumnLayout { description: qsTr("Uses about 2GB.") recommended: true checked: true - detail: ProgressIndicator { - implicitWidth: 75 - progress: 0.25 - } } OptionButton { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Default") description: qsTr("Uses about 423GB.") - detail: ProgressIndicator { - implicitWidth: 75 - progress: 0.8 - } } } diff --git a/src/qml/controls/OptionButton.qml b/src/qml/controls/OptionButton.qml index 97485fb1eb..b40c9b752e 100644 --- a/src/qml/controls/OptionButton.qml +++ b/src/qml/controls/OptionButton.qml @@ -12,7 +12,6 @@ Button { id: button padding: 15 checkable: true - property alias detail: detail_loader.sourceComponent implicitWidth: 450 background: Rectangle { border.width: 1 @@ -67,7 +66,15 @@ Button { } Loader { id: detail_loader - visible: item + visible: button.checked + active: true + sourceComponent: Button { + icon.source: "image://images/check" + icon.color: Theme.color.neutral9 + icon.height: 24 + icon.width: 24 + background: null + } } } } From 5b00b0e477c8532974457fee7b1ad2cbbdc0ecc9 Mon Sep 17 00:00:00 2001 From: Jarol Rodriguez Date: Wed, 14 Dec 2022 01:53:08 -0500 Subject: [PATCH 03/17] qml: storage options description fixups --- src/qml/components/StorageLocations.qml | 8 ++++---- src/qml/components/StorageOptions.qml | 6 +++--- src/qml/pages/onboarding/OnboardingStorageLocation.qml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qml/components/StorageLocations.qml b/src/qml/components/StorageLocations.qml index fde4e0a8af..241477e775 100644 --- a/src/qml/components/StorageLocations.qml +++ b/src/qml/components/StorageLocations.qml @@ -15,15 +15,15 @@ ColumnLayout { OptionButton { Layout.fillWidth: true ButtonGroup.group: group - text: qsTr("Default directory") - description: qsTr("The downloaded block data will be saved to the default data directory for your OS.") + text: qsTr("Default") + description: qsTr("Your application directory.") recommended: true checked: true } OptionButton { Layout.fillWidth: true ButtonGroup.group: group - text: qsTr("Custom directory") - description: qsTr("The downloaded block data will be saved to the chosen directory.") + text: qsTr("Custom") + description: qsTr("Choose the directory and storage device.") } } diff --git a/src/qml/components/StorageOptions.qml b/src/qml/components/StorageOptions.qml index 9c30e0f0de..fbd1f2aa33 100644 --- a/src/qml/components/StorageOptions.qml +++ b/src/qml/components/StorageOptions.qml @@ -16,14 +16,14 @@ ColumnLayout { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Reduce storage") - description: qsTr("Uses about 2GB.") + description: qsTr("Uses about 2GB. For simple wallet use.") recommended: true checked: true } OptionButton { Layout.fillWidth: true ButtonGroup.group: group - text: qsTr("Default") - description: qsTr("Uses about 423GB.") + text: qsTr("Store all data") + description: qsTr("Uses about 550GB. Support the network.") } } diff --git a/src/qml/pages/onboarding/OnboardingStorageLocation.qml b/src/qml/pages/onboarding/OnboardingStorageLocation.qml index 030bbebfc9..c4d2f770f9 100644 --- a/src/qml/pages/onboarding/OnboardingStorageLocation.qml +++ b/src/qml/pages/onboarding/OnboardingStorageLocation.qml @@ -19,7 +19,7 @@ InformationPage { bold: true headerText: qsTr("Storage location") headerMargin: 0 - description: qsTr("Where do you want to store the downloaded block data?") + description: qsTr("Where do you want to store the downloaded block data?\nYou need a minimum of 1GB of storage.") descriptionMargin: 20 detailActive: true detailItem: ColumnLayout { From 03403913b301a631fc07a2b3c737ecad142f2284 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Wed, 26 Oct 2022 09:54:36 +0100 Subject: [PATCH 04/17] qml, build: Add `OptionsQmlModel` class with basic implementation --- src/Makefile.qt.include | 3 +++ src/qml/bitcoin.cpp | 4 ++++ src/qml/options_model.cpp | 12 ++++++++++++ src/qml/options_model.h | 26 ++++++++++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 src/qml/options_model.cpp create mode 100644 src/qml/options_model.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 8f7e560357..749ab2c614 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -37,6 +37,7 @@ QT_FORMS_UI = \ QT_MOC_CPP = \ qml/moc_appmode.cpp \ qml/moc_nodemodel.cpp \ + qml/moc_options_model.cpp \ qt/moc_addressbookpage.cpp \ qt/moc_addresstablemodel.cpp \ qt/moc_askpassphrasedialog.cpp \ @@ -112,6 +113,7 @@ BITCOIN_QT_H = \ qml/bitcoin.h \ qml/imageprovider.h \ qml/nodemodel.h \ + qml/options_model.h \ qml/util.h \ qt/addressbookpage.h \ qt/addresstablemodel.h \ @@ -291,6 +293,7 @@ BITCOIN_QML_BASE_CPP = \ qml/bitcoin.cpp \ qml/imageprovider.cpp \ qml/nodemodel.cpp \ + qml/options_model.cpp \ qml/util.cpp QML_RES_FONTS = \ diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 489171d3ed..8391161b30 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,9 @@ int QmlGuiMain(int argc, char* argv[]) engine.rootContext()->setContextProperty("nodeModel", &node_model); + OptionsQmlModel options_model{*node}; + engine.rootContext()->setContextProperty("options", &options_model); + #ifdef __ANDROID__ AppMode app_mode(AppMode::MOBILE); #else diff --git a/src/qml/options_model.cpp b/src/qml/options_model.cpp new file mode 100644 index 0000000000..7406fb946d --- /dev/null +++ b/src/qml/options_model.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +OptionsQmlModel::OptionsQmlModel(interfaces::Node& node) + : m_node{node} +{ +} diff --git a/src/qml/options_model.h b/src/qml/options_model.h new file mode 100644 index 0000000000..d405d9b0db --- /dev/null +++ b/src/qml/options_model.h @@ -0,0 +1,26 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QML_OPTIONS_MODEL_H +#define BITCOIN_QML_OPTIONS_MODEL_H + +#include + +namespace interfaces { +class Node; +} + +/** Model for Bitcoin client options. */ +class OptionsQmlModel : public QObject +{ + Q_OBJECT + +public: + explicit OptionsQmlModel(interfaces::Node& node); + +private: + interfaces::Node& m_node; +}; + +#endif // BITCOIN_QML_OPTIONS_MODEL_H From 52dfd52ca17af921ccb92be01b0701f31a27aca7 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Wed, 14 Dec 2022 03:58:48 -0500 Subject: [PATCH 05/17] qml: Add "-prune" settings to `OptionsQmlModel` class --- src/qml/options_model.cpp | 34 ++++++++++++++++++++++++++++++++++ src/qml/options_model.h | 19 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/qml/options_model.cpp b/src/qml/options_model.cpp index 7406fb946d..ed934121d4 100644 --- a/src/qml/options_model.cpp +++ b/src/qml/options_model.cpp @@ -5,8 +5,42 @@ #include #include +#include +#include +#include +#include +#include + +#include OptionsQmlModel::OptionsQmlModel(interfaces::Node& node) : m_node{node} { + int64_t prune_value{SettingToInt(m_node.getPersistentSetting("prune"), 0)}; + m_prune = (prune_value > 1); + m_prune_size_gb = m_prune ? PruneMiBtoGB(prune_value) : DEFAULT_PRUNE_TARGET_GB; +} + +void OptionsQmlModel::setPrune(bool new_prune) +{ + if (new_prune != m_prune) { + m_prune = new_prune; + m_node.updateRwSetting("prune", pruneSetting()); + Q_EMIT pruneChanged(new_prune); + } +} + +void OptionsQmlModel::setPruneSizeGB(int new_prune_size_gb) +{ + if (new_prune_size_gb != m_prune_size_gb) { + m_prune_size_gb = new_prune_size_gb; + m_node.updateRwSetting("prune", pruneSetting()); + Q_EMIT pruneSizeGBChanged(new_prune_size_gb); + } +} + +util::SettingsValue OptionsQmlModel::pruneSetting() const +{ + assert(!m_prune || m_prune_size_gb >= 1); + return m_prune ? PruneGBtoMiB(m_prune_size_gb) : 0; } diff --git a/src/qml/options_model.h b/src/qml/options_model.h index d405d9b0db..7e227ee6b1 100644 --- a/src/qml/options_model.h +++ b/src/qml/options_model.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_QML_OPTIONS_MODEL_H #define BITCOIN_QML_OPTIONS_MODEL_H +#include + #include namespace interfaces { @@ -15,12 +17,29 @@ class Node; class OptionsQmlModel : public QObject { Q_OBJECT + Q_PROPERTY(bool prune READ prune WRITE setPrune NOTIFY pruneChanged) + Q_PROPERTY(int pruneSizeGB READ pruneSizeGB WRITE setPruneSizeGB NOTIFY pruneSizeGBChanged) public: explicit OptionsQmlModel(interfaces::Node& node); + bool prune() const { return m_prune; } + void setPrune(bool new_prune); + int pruneSizeGB() const { return m_prune_size_gb; } + void setPruneSizeGB(int new_prune_size); + +Q_SIGNALS: + void pruneChanged(bool new_prune); + void pruneSizeGBChanged(int new_prune_size_gb); + private: interfaces::Node& m_node; + + // Properties that are exposed to QML. + bool m_prune; + int m_prune_size_gb; + + util::SettingsValue pruneSetting() const; }; #endif // BITCOIN_QML_OPTIONS_MODEL_H From 12cbdc929d534225adf8f294adf2c8c665f69852 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Wed, 14 Dec 2022 03:59:14 -0500 Subject: [PATCH 06/17] qml: Wire Storage amount options to OptionsModel backend Wires both the easy and advanced configuration options related to storage amount to the options model backend. Additionally, this removes an unused and unwanted storage location setting from the advanced storage amount settings table. --- src/qml/components/StorageOptions.qml | 7 +++++++ src/qml/components/StorageSettings.qml | 17 +++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/qml/components/StorageOptions.qml b/src/qml/components/StorageOptions.qml index fbd1f2aa33..01f0bd2419 100644 --- a/src/qml/components/StorageOptions.qml +++ b/src/qml/components/StorageOptions.qml @@ -19,11 +19,18 @@ ColumnLayout { description: qsTr("Uses about 2GB. For simple wallet use.") recommended: true checked: true + onClicked: { + options.prune = true + options.pruneSizeGB = 2 + } } OptionButton { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Store all data") description: qsTr("Uses about 550GB. Support the network.") + onClicked: { + options.prune = false + } } } diff --git a/src/qml/components/StorageSettings.qml b/src/qml/components/StorageSettings.qml index 90c16f38dd..b4b3263231 100644 --- a/src/qml/components/StorageSettings.qml +++ b/src/qml/components/StorageSettings.qml @@ -11,21 +11,18 @@ ColumnLayout { spacing: 20 Setting { Layout.fillWidth: true - header: qsTr("Store Recent blocks only") - actionItem: OptionSwitch {} - } - Setting { - Layout.fillWidth: true - header: qsTr("Storage limit") - actionItem: ValueInput { - description: qsTr("2 GB") + header: qsTr("Store recent blocks only") + actionItem: OptionSwitch { + checked: options.prune + onToggled: options.prune = checked } } Setting { Layout.fillWidth: true - header: qsTr("Data location") + header: qsTr("Storage limit (GB)") actionItem: ValueInput { - description: qsTr("c://.../data") + description: options.pruneSizeGB + onEditingFinished: options.pruneSizeGB = parseInt(text) } } } From d58e392782cfafae1d184219814370751add2570 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Wed, 1 Jun 2022 22:03:31 +0200 Subject: [PATCH 07/17] net: Notify about connection number by type --- src/interfaces/node.h | 3 ++- src/net.cpp | 42 ++++++++++++++++++++++++++++++++++----- src/net.h | 7 ++++++- src/node/interface_ui.cpp | 2 +- src/node/interface_ui.h | 10 +++++++++- src/qt/clientmodel.cpp | 5 +++-- 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/interfaces/node.h b/src/interfaces/node.h index dbdb21eb91..c64e5e249b 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -33,6 +33,7 @@ enum class SynchronizationState; enum class TransactionError; struct CNodeStateStats; struct bilingual_str; +struct PeersNumByType; namespace node { struct NodeContext; } // namespace node @@ -238,7 +239,7 @@ class Node virtual std::unique_ptr handleInitWallet(InitWalletFn fn) = 0; //! Register handler for number of connections changed messages. - using NotifyNumConnectionsChangedFn = std::function; + using NotifyNumConnectionsChangedFn = std::function; virtual std::unique_ptr handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0; //! Register handler for network active messages. diff --git a/src/net.cpp b/src/net.cpp index 374e93a2bd..3b4d829ec2 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1133,15 +1133,47 @@ void CConnman::DisconnectNodes() void CConnman::NotifyNumConnectionsChanged() { - size_t nodes_size; + decltype(m_nodes) nodes_copy; { LOCK(m_nodes_mutex); - nodes_size = m_nodes.size(); + nodes_copy = m_nodes; } - if(nodes_size != nPrevNodeCount) { - nPrevNodeCount = nodes_size; + + int num_outbound_full_relay{0}; + int num_block_relay{0}; + int num_manual{0}; + int num_inbound{0}; + for (const auto* node : nodes_copy) { + switch (node->m_conn_type) { + case ConnectionType::OUTBOUND_FULL_RELAY: + ++num_outbound_full_relay; + break; + case ConnectionType::BLOCK_RELAY: + ++num_block_relay; + break; + case ConnectionType::MANUAL: + ++num_manual; + break; + case ConnectionType::INBOUND: + ++num_inbound; + break; + case ConnectionType::FEELER: + case ConnectionType::ADDR_FETCH: + break; + } + } + + if (num_outbound_full_relay != m_num_outbound_full_relay || + num_block_relay != m_num_block_relay || + num_manual != m_num_manual || + num_inbound != m_num_inbound) { + m_num_outbound_full_relay = num_outbound_full_relay; + m_num_block_relay = num_block_relay; + m_num_manual = num_manual; + m_num_inbound = num_inbound; if (m_client_interface) { - m_client_interface->NotifyNumConnectionsChanged(nodes_size); + m_client_interface->NotifyNumConnectionsChanged( + {num_outbound_full_relay, num_block_relay, num_manual, num_inbound, static_cast(nodes_copy.size())}); } } } diff --git a/src/net.h b/src/net.h index 44641fb47c..162018992a 100644 --- a/src/net.h +++ b/src/net.h @@ -46,6 +46,7 @@ class BanMan; class CNode; class CScheduler; struct bilingual_str; +struct PeersNumByType; /** Default for -whitelistrelay. */ static const bool DEFAULT_WHITELISTRELAY = true; @@ -1010,7 +1011,11 @@ class CConnman std::list m_nodes_disconnected; mutable RecursiveMutex m_nodes_mutex; std::atomic nLastNodeId{0}; - unsigned int nPrevNodeCount{0}; + int m_num_outbound_full_relay{0}; + int m_num_block_relay{0}; + int m_num_manual{0}; + int m_num_inbound{0}; + /** * Cache responses to addr requests to minimize privacy leak. diff --git a/src/node/interface_ui.cpp b/src/node/interface_ui.cpp index fa90d6fda7..836455b25f 100644 --- a/src/node/interface_ui.cpp +++ b/src/node/interface_ui.cpp @@ -48,7 +48,7 @@ bool CClientUIInterface::ThreadSafeMessageBox(const bilingual_str& message, cons bool CClientUIInterface::ThreadSafeQuestion(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style).value_or(false);} void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_signals.InitMessage(message); } void CClientUIInterface::InitWallet() { return g_ui_signals.InitWallet(); } -void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); } +void CClientUIInterface::NotifyNumConnectionsChanged(PeersNumByType newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); } void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); } void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); } void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); } diff --git a/src/node/interface_ui.h b/src/node/interface_ui.h index 316d75167e..c11562be61 100644 --- a/src/node/interface_ui.h +++ b/src/node/interface_ui.h @@ -20,6 +20,14 @@ class connection; } } // namespace boost +struct PeersNumByType { + int outbound_full_relay{0}; + int block_relay{0}; + int manual{0}; + int inbound{0}; + int total{0}; +}; + /** Signals for UI communication. */ class CClientUIInterface { @@ -85,7 +93,7 @@ class CClientUIInterface ADD_SIGNALS_DECL_WRAPPER(InitWallet, void, ); /** Number of network connections changed. */ - ADD_SIGNALS_DECL_WRAPPER(NotifyNumConnectionsChanged, void, int newNumConnections); + ADD_SIGNALS_DECL_WRAPPER(NotifyNumConnectionsChanged, void, PeersNumByType newNumConnections); /** Network activity state changed. */ ADD_SIGNALS_DECL_WRAPPER(NotifyNetworkActiveChanged, void, bool networkActive); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 092ffe7e5b..994aaba40d 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -245,8 +246,8 @@ void ClientModel::subscribeToCoreSignals() Q_EMIT showProgress(QString::fromStdString(title), progress); }); m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged( - [this](int new_num_connections) { - Q_EMIT numConnectionsChanged(new_num_connections); + [this](PeersNumByType new_num_connections) { + Q_EMIT numConnectionsChanged(new_num_connections.total); }); m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged( [this](bool network_active) { From ff02ede31a0f2f74fe1cab97e06ab663325c44a8 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Fri, 3 Jun 2022 19:23:50 +0200 Subject: [PATCH 08/17] qml: Add `NodeModel::numOutboundPeers` property --- src/qml/nodemodel.cpp | 21 +++++++++++++++++++++ src/qml/nodemodel.h | 10 ++++++++++ src/qt/clientmodel.cpp | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/qml/nodemodel.cpp b/src/qml/nodemodel.cpp index 862597779d..9ad50a0908 100644 --- a/src/qml/nodemodel.cpp +++ b/src/qml/nodemodel.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include @@ -17,6 +19,7 @@ NodeModel::NodeModel(interfaces::Node& node) : m_node{node} { ConnectToBlockTipSignal(); + ConnectToNumConnectionsChangedSignal(); } void NodeModel::setBlockTipHeight(int new_height) @@ -27,6 +30,14 @@ void NodeModel::setBlockTipHeight(int new_height) } } +void NodeModel::setNumOutboundPeers(int new_num) +{ + if (new_num != m_num_outbound_peers) { + m_num_outbound_peers = new_num; + Q_EMIT numOutboundPeersChanged(); + } +} + void NodeModel::setVerificationProgress(double new_progress) { if (new_progress != m_verification_progress) { @@ -78,3 +89,13 @@ void NodeModel::ConnectToBlockTipSignal() }); }); } + +void NodeModel::ConnectToNumConnectionsChangedSignal() +{ + assert(!m_handler_notify_num_peers_changed); + + m_handler_notify_num_peers_changed = m_node.handleNotifyNumConnectionsChanged( + [this](PeersNumByType new_num_peers) { + setNumOutboundPeers(new_num_peers.outbound_full_relay + new_num_peers.block_relay); + }); +} diff --git a/src/qml/nodemodel.h b/src/qml/nodemodel.h index c6a04a1f2c..2cd3661f2f 100644 --- a/src/qml/nodemodel.h +++ b/src/qml/nodemodel.h @@ -25,6 +25,8 @@ class NodeModel : public QObject { Q_OBJECT Q_PROPERTY(int blockTipHeight READ blockTipHeight NOTIFY blockTipHeightChanged) + Q_PROPERTY(int numOutboundPeers READ numOutboundPeers NOTIFY numOutboundPeersChanged) + Q_PROPERTY(int maxNumOutboundPeers READ maxNumOutboundPeers CONSTANT) Q_PROPERTY(double verificationProgress READ verificationProgress NOTIFY verificationProgressChanged) public: @@ -32,6 +34,9 @@ class NodeModel : public QObject int blockTipHeight() const { return m_block_tip_height; } void setBlockTipHeight(int new_height); + int numOutboundPeers() const { return m_num_outbound_peers; } + void setNumOutboundPeers(int new_num); + int maxNumOutboundPeers() const { return m_max_num_outbound_peers; } double verificationProgress() const { return m_verification_progress; } void setVerificationProgress(double new_progress); @@ -45,6 +50,7 @@ public Q_SLOTS: Q_SIGNALS: void blockTipHeightChanged(); + void numOutboundPeersChanged(); void requestedInitialize(); void requestedShutdown(); void verificationProgressChanged(); @@ -55,14 +61,18 @@ public Q_SLOTS: private: // Properties that are exposed to QML. int m_block_tip_height{0}; + int m_num_outbound_peers{0}; + static constexpr int m_max_num_outbound_peers{MAX_OUTBOUND_FULL_RELAY_CONNECTIONS + MAX_BLOCK_RELAY_ONLY_CONNECTIONS}; double m_verification_progress{0.0}; int m_shutdown_polling_timer_id{0}; interfaces::Node& m_node; std::unique_ptr m_handler_notify_block_tip; + std::unique_ptr m_handler_notify_num_peers_changed; void ConnectToBlockTipSignal(); + void ConnectToNumConnectionsChangedSignal(); }; #endif // BITCOIN_QML_NODEMODEL_H diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 994aaba40d..f0ce083fba 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include From 4197d59ecb4f2ff1abf7097e8a70451e661907e4 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Fri, 3 Jun 2022 17:45:01 +0200 Subject: [PATCH 09/17] qml: Add `PeersIndicator` component Co-authored-by: Christoph Ono --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/components/PeersIndicator.qml | 41 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 src/qml/components/PeersIndicator.qml diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 749ab2c614..a653974229 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -326,6 +326,7 @@ QML_RES_QML = \ qml/components/ConnectionOptions.qml \ qml/components/ConnectionSettings.qml \ qml/components/DeveloperOptions.qml \ + qml/components/PeersIndicator.qml \ qml/components/StorageLocations.qml \ qml/components/StorageOptions.qml \ qml/components/StorageSettings.qml \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index d1d1a84b1f..0617cef3cc 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -5,6 +5,7 @@ components/ConnectionOptions.qml components/ConnectionSettings.qml components/DeveloperOptions.qml + components/PeersIndicator.qml components/StorageLocations.qml components/StorageOptions.qml components/StorageSettings.qml diff --git a/src/qml/components/PeersIndicator.qml b/src/qml/components/PeersIndicator.qml new file mode 100644 index 0000000000..2aafbb1851 --- /dev/null +++ b/src/qml/components/PeersIndicator.qml @@ -0,0 +1,41 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// The PeersIndicator component. +// See reference design: https://bitcoindesign.github.io/Bitcoin-Core-App/assets/images/block-clock-connection-states-big.png + +import QtQuick 2.15 +import "../controls" + +Item { + id: root + required property int numOutboundPeers + required property int maxNumOutboundPeers + + implicitWidth: dots.childrenRect.width + implicitHeight: dots.childrenRect.height + + Row { + id: dots + property int size: 5 + spacing: 5 + Repeater { + model: 5 + Rectangle { + width: 3 + height: 3 + radius: width / 2 + color: Theme.color.neutral9 + opacity: (index === 0 && root.numOutboundPeers > 0) || (index + 1 <= dots.size * root.numOutboundPeers / root.maxNumOutboundPeers) ? 0.95 : 0.45 + Behavior on opacity { OpacityAnimator { duration: 100 } } + SequentialAnimation on opacity { + loops: Animation.Infinite + running: numOutboundPeers === 0 && index === 0 + SmoothedAnimation { to: 0; velocity: 2.2 } + SmoothedAnimation { to: 1; velocity: 2.2 } + } + } + } + } +} From 13c052ea57eaa0eafcb257dc923784ed56e7b151 Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 28 Nov 2022 17:34:36 +0530 Subject: [PATCH 10/17] Add getBlockTime interface method in src/interfaces/chain.h - This method would allow to get the blocktime value of the block at the given height. --- src/interfaces/chain.h | 3 +++ src/node/interfaces.cpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 7a3d88b18f..25524b8b65 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -128,6 +128,9 @@ class Chain //! Get block hash. Height must be valid or this function will abort. virtual uint256 getBlockHash(int height) = 0; + //! Get block time, Height must be valid or this function will abort. + virtual int64_t getBlockTime(int height) = 0; + //! Check that the block is available on disk (i.e. has not been //! pruned), and contains transactions. virtual bool haveBlockOnDisk(int height) = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 7a15f3649b..3d461ac9e0 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -514,6 +514,11 @@ class ChainImpl : public Chain LOCK(::cs_main); return Assert(chainman().ActiveChain()[height])->GetBlockHash(); } + int64_t getBlockTime(int height) override + { + LOCK(::cs_main); + return Assert(chainman().ActiveChain()[height])->GetBlockTime(); + } bool haveBlockOnDisk(int height) override { LOCK(::cs_main); From 2ba086f2e6850d965693e28f51e643f05bbf431d Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 28 Nov 2022 19:08:47 +0530 Subject: [PATCH 11/17] qml: Introduced a new class chainmodel - This file allowing using of Chain interfaces to get information about the blockchain. - This class declares and defines the function that will be used in for the Block Clock. --- src/Makefile.qt.include | 3 ++ src/qml/chainmodel.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ src/qml/chainmodel.h | 50 +++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/qml/chainmodel.cpp create mode 100644 src/qml/chainmodel.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index a653974229..c86a96df05 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -36,6 +36,7 @@ QT_FORMS_UI = \ QT_MOC_CPP = \ qml/moc_appmode.cpp \ + qml/moc_chainmodel.cpp \ qml/moc_nodemodel.cpp \ qml/moc_options_model.cpp \ qt/moc_addressbookpage.cpp \ @@ -111,6 +112,7 @@ QT_QRC_LOCALE = qt/bitcoin_locale.qrc BITCOIN_QT_H = \ qml/appmode.h \ qml/bitcoin.h \ + qml/chainmodel.h \ qml/imageprovider.h \ qml/nodemodel.h \ qml/options_model.h \ @@ -291,6 +293,7 @@ BITCOIN_QT_WALLET_CPP = \ BITCOIN_QML_BASE_CPP = \ qml/bitcoin.cpp \ + qml/chainmodel.cpp \ qml/imageprovider.cpp \ qml/nodemodel.cpp \ qml/options_model.cpp \ diff --git a/src/qml/chainmodel.cpp b/src/qml/chainmodel.cpp new file mode 100644 index 0000000000..343cd5a21b --- /dev/null +++ b/src/qml/chainmodel.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include + +ChainModel::ChainModel(interfaces::Chain& chain) + : m_chain{chain} +{ + timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &ChainModel::setCurrentTimeRatio); + timer->start(1000); + + QThread* timer_thread = new QThread; + timer->moveToThread(timer_thread); + timer_thread->start(); +} + +void ChainModel::setTimeRatioList(int new_time) +{ + int timeAtMeridian = timestampAtMeridian(); + + if (new_time < timeAtMeridian) { + return; + } + + m_time_ratio_list.push_back(double(new_time - timeAtMeridian) / SECS_IN_12_HOURS); + + Q_EMIT timeRatioListChanged(); +} + +int ChainModel::timestampAtMeridian() +{ + int secsSinceMeridian = (QTime::currentTime().msecsSinceStartOfDay() / 1000) % SECS_IN_12_HOURS; + int currentTimestamp = QDateTime::currentSecsSinceEpoch(); + + return currentTimestamp - secsSinceMeridian; +} + +void ChainModel::setTimeRatioListInitial() +{ + int timeAtMeridian = timestampAtMeridian(); + int first_block_height; + int active_chain_height = m_chain.getHeight().value(); + bool success = m_chain.findFirstBlockWithTimeAndHeight(/*min_time=*/timeAtMeridian, /*min_height=*/0, interfaces::FoundBlock().height(first_block_height)); + + if (!success) { + return; + } + + m_time_ratio_list.clear(); + m_time_ratio_list.push_back(double(QDateTime::currentSecsSinceEpoch() - timeAtMeridian) / SECS_IN_12_HOURS); + + for (int height = first_block_height; height < active_chain_height + 1; height++) { + m_time_ratio_list.push_back(double(m_chain.getBlockTime(height) - timeAtMeridian) / SECS_IN_12_HOURS); + } + + Q_EMIT timeRatioListChanged(); +} + +void ChainModel::setCurrentTimeRatio() +{ + int secsSinceMeridian = (QTime::currentTime().msecsSinceStartOfDay() / 1000) % SECS_IN_12_HOURS; + double currentTimeRatio = double(secsSinceMeridian) / SECS_IN_12_HOURS; + + if (currentTimeRatio < m_time_ratio_list[0].toDouble()) { // That means time has crossed a meridian + m_time_ratio_list.erase(m_time_ratio_list.begin() + 1, m_time_ratio_list.end()); + } + m_time_ratio_list[0] = currentTimeRatio; +} \ No newline at end of file diff --git a/src/qml/chainmodel.h b/src/qml/chainmodel.h new file mode 100644 index 0000000000..2943c9c160 --- /dev/null +++ b/src/qml/chainmodel.h @@ -0,0 +1,50 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QML_CHAINMODEL_H +#define BITCOIN_QML_CHAINMODEL_H + +#include + +#include +#include +#include + +namespace interfaces { +class FoundBlock; +class Chain; +} // namespace interfaces + +static const int SECS_IN_12_HOURS = 43200; + +class ChainModel : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariantList timeRatioList READ timeRatioList NOTIFY timeRatioListChanged) + +public: + explicit ChainModel(interfaces::Chain& chain); + + QVariantList timeRatioList() const { return m_time_ratio_list; }; + + int timestampAtMeridian(); + + void setCurrentTimeRatio(); + +public Q_SLOTS: + void setTimeRatioList(int new_time); + void setTimeRatioListInitial(); + +Q_SIGNALS: + void timeRatioListChanged(); + +private: + QVariantList m_time_ratio_list{0.0}; + + QTimer* timer; + + interfaces::Chain& m_chain; +}; + +#endif // BITCOIN_QML_CHAINMODEL_H From cd3aeb497d765556642ffd2b93dfeab6907d7f98 Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 28 Nov 2022 19:19:51 +0530 Subject: [PATCH 12/17] qml: Made few additions to nodemodel class 1. Added a new Q_PROPERTY to track the remaining time till the IBD completioni. 2. Added two signals to later connect to the chainmodel class function. 3. Added new member variable, m_pause, which will keep track of if the block clock is in paused state or not, and will accordingly disconnect or reconnect the user to network. --- src/qml/nodemodel.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++ src/qml/nodemodel.h | 15 +++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/qml/nodemodel.cpp b/src/qml/nodemodel.cpp index 9ad50a0908..a10f07c73f 100644 --- a/src/qml/nodemodel.cpp +++ b/src/qml/nodemodel.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -38,14 +39,60 @@ void NodeModel::setNumOutboundPeers(int new_num) } } +void NodeModel::setRemainingSyncTime(double new_progress) +{ + int currentTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // keep a vector of samples of verification progress at height + m_block_process_time.push_front(qMakePair(currentTime, new_progress)); + + // show progress speed if we have more than one sample + if (m_block_process_time.size() >= 2) { + double progressDelta = 0; + int timeDelta = 0; + int remainingMSecs = 0; + double remainingProgress = 1.0 - new_progress; + for (int i = 1; i < m_block_process_time.size(); i++) { + QPair sample = m_block_process_time[i]; + + // take first sample after 500 seconds or last available one + if (sample.first < (currentTime - 500 * 1000) || i == m_block_process_time.size() - 1) { + progressDelta = m_block_process_time[0].second - sample.second; + timeDelta = m_block_process_time[0].first - sample.first; + remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; + break; + } + } + if (remainingMSecs > 0 && m_block_process_time.count() % 1000 == 0) { + m_remaining_sync_time = remainingMSecs; + + Q_EMIT remainingSyncTimeChanged(); + } + static const int MAX_SAMPLES = 5000; + if (m_block_process_time.count() > MAX_SAMPLES) { + m_block_process_time.remove(1, m_block_process_time.count() - 1); + } + } +} void NodeModel::setVerificationProgress(double new_progress) { if (new_progress != m_verification_progress) { + setRemainingSyncTime(new_progress); + m_verification_progress = new_progress; Q_EMIT verificationProgressChanged(); } } +void NodeModel::setPause(bool new_pause) +{ + if(m_pause != new_pause) { + m_pause = new_pause; + m_node.setNetworkActive(!new_pause); + Q_EMIT pauseChanged(new_pause); + } +} + void NodeModel::startNodeInitializionThread() { Q_EMIT requestedInitialize(); @@ -56,6 +103,8 @@ void NodeModel::initializeResult([[maybe_unused]] bool success, interfaces::Bloc // TODO: Handle the `success` parameter, setBlockTipHeight(tip_info.block_height); setVerificationProgress(tip_info.verification_progress); + + Q_EMIT setTimeRatioListInitial(); } void NodeModel::startShutdownPolling() @@ -86,6 +135,8 @@ void NodeModel::ConnectToBlockTipSignal() QMetaObject::invokeMethod(this, [=] { setBlockTipHeight(tip.block_height); setVerificationProgress(verification_progress); + + Q_EMIT setTimeRatioList(tip.block_time); }); }); } diff --git a/src/qml/nodemodel.h b/src/qml/nodemodel.h index 2cd3661f2f..90b4872e92 100644 --- a/src/qml/nodemodel.h +++ b/src/qml/nodemodel.h @@ -27,7 +27,9 @@ class NodeModel : public QObject Q_PROPERTY(int blockTipHeight READ blockTipHeight NOTIFY blockTipHeightChanged) Q_PROPERTY(int numOutboundPeers READ numOutboundPeers NOTIFY numOutboundPeersChanged) Q_PROPERTY(int maxNumOutboundPeers READ maxNumOutboundPeers CONSTANT) + Q_PROPERTY(int remainingSyncTime READ remainingSyncTime NOTIFY remainingSyncTimeChanged) Q_PROPERTY(double verificationProgress READ verificationProgress NOTIFY verificationProgressChanged) + Q_PROPERTY(bool pause READ pause WRITE setPause NOTIFY pauseChanged) public: explicit NodeModel(interfaces::Node& node); @@ -37,8 +39,12 @@ class NodeModel : public QObject int numOutboundPeers() const { return m_num_outbound_peers; } void setNumOutboundPeers(int new_num); int maxNumOutboundPeers() const { return m_max_num_outbound_peers; } + int remainingSyncTime() const { return m_remaining_sync_time; } + void setRemainingSyncTime(double new_progress); double verificationProgress() const { return m_verification_progress; } void setVerificationProgress(double new_progress); + bool pause() const { return m_pause; } + void setPause(bool new_pause); Q_INVOKABLE void startNodeInitializionThread(); @@ -51,9 +57,14 @@ public Q_SLOTS: Q_SIGNALS: void blockTipHeightChanged(); void numOutboundPeersChanged(); + void remainingSyncTimeChanged(); void requestedInitialize(); void requestedShutdown(); void verificationProgressChanged(); + void pauseChanged(bool new_pause); + + void setTimeRatioList(int new_time); + void setTimeRatioListInitial(); protected: void timerEvent(QTimerEvent* event) override; @@ -63,10 +74,14 @@ public Q_SLOTS: int m_block_tip_height{0}; int m_num_outbound_peers{0}; static constexpr int m_max_num_outbound_peers{MAX_OUTBOUND_FULL_RELAY_CONNECTIONS + MAX_BLOCK_RELAY_ONLY_CONNECTIONS}; + int m_remaining_sync_time{0}; double m_verification_progress{0.0}; + bool m_pause{false}; int m_shutdown_polling_timer_id{0}; + QVector> m_block_process_time; + interfaces::Node& m_node; std::unique_ptr m_handler_notify_block_tip; std::unique_ptr m_handler_notify_num_peers_changed; From ea665c1cb871fa260029cfd17459f3f1348b162a Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 28 Nov 2022 19:56:42 +0530 Subject: [PATCH 13/17] qml: Initialised the chainmodel in src/bitcoin.cpp - Also made appropriate connections with the nodemodel signals --- src/qml/bitcoin.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 8391161b30..c2c1f0c115 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -6,12 +6,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -155,6 +157,7 @@ int QmlGuiMain(int argc, char* argv[]) GUIUtil::LogQtInfo(); std::unique_ptr node = init->makeNode(); + std::unique_ptr chain = init->makeChain(); if (!node->baseInitialize()) { // A dialog with detailed error will have been shown by InitError(). return EXIT_FAILURE; @@ -189,6 +192,12 @@ int QmlGuiMain(int argc, char* argv[]) OptionsQmlModel options_model{*node}; engine.rootContext()->setContextProperty("options", &options_model); + ChainModel chain_model{*chain}; + engine.rootContext()->setContextProperty("chainModel", &chain_model); + + QObject::connect(&node_model, &NodeModel::setTimeRatioList, &chain_model, &ChainModel::setTimeRatioList); + QObject::connect(&node_model, &NodeModel::setTimeRatioListInitial, &chain_model, &ChainModel::setTimeRatioListInitial); + #ifdef __ANDROID__ AppMode app_mode(AppMode::MOBILE); #else From b5d2b25ad6f6f51a59a579640ed7a051e77c3dab Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 28 Nov 2022 20:15:59 +0530 Subject: [PATCH 14/17] qml: Added descriptionColor property in Header.qml - This property will later be used for the BlockClock.qml file --- src/qml/controls/Header.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qml/controls/Header.qml b/src/qml/controls/Header.qml index 84ef0e771f..587a6ed558 100644 --- a/src/qml/controls/Header.qml +++ b/src/qml/controls/Header.qml @@ -16,6 +16,7 @@ ColumnLayout { property string description: "" property int descriptionMargin: 10 property int descriptionSize: 18 + property string descriptionColor: Theme.color.neutral8 property string subtext: "" property int subtextMargin property int subtextSize: 15 @@ -42,7 +43,7 @@ ColumnLayout { font.family: "Inter" font.styleName: "Regular" font.pixelSize: root.descriptionSize - color: Theme.color.neutral8 + color: root.descriptionColor text: root.description horizontalAlignment: root.center ? Text.AlignHCenter : Text.AlignLeft wrapMode: wrap ? Text.WordWrap : Text.NoWrap From 1bd29f5013a8e75e64882ca0e6136e412965567e Mon Sep 17 00:00:00 2001 From: jarolrod Date: Wed, 14 Dec 2022 04:59:34 -0500 Subject: [PATCH 15/17] qml: Introduce the BlockClock.qml and BlockClockComponent.qml files - The BlockClock.qml files provides the control for BlockClock and determines how the elements would be displayed. - The BlockClockComponent.qml file uses the aforementioned control to display the value, and determines what will be displayed. --- src/Makefile.qt.include | 2 + src/qml/bitcoin_qml.qrc | 2 + src/qml/components/BlockClockComponent.qml | 61 ++++++++ src/qml/controls/BlockClock.qml | 157 +++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 src/qml/components/BlockClockComponent.qml create mode 100644 src/qml/controls/BlockClock.qml diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c86a96df05..f1b1095dfd 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -326,6 +326,7 @@ QML_QRC = qml/bitcoin_qml.qrc QML_RES_QML = \ qml/components/AboutOptions.qml \ qml/components/BlockCounter.qml \ + qml/components/BlockClockComponent.qml \ qml/components/ConnectionOptions.qml \ qml/components/ConnectionSettings.qml \ qml/components/DeveloperOptions.qml \ @@ -333,6 +334,7 @@ QML_RES_QML = \ qml/components/StorageLocations.qml \ qml/components/StorageOptions.qml \ qml/components/StorageSettings.qml \ + qml/controls/BlockClock.qml \ qml/controls/ContinueButton.qml \ qml/controls/ExternalLink.qml \ qml/controls/Header.qml \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 0617cef3cc..e0748d13e1 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -1,6 +1,7 @@ components/AboutOptions.qml + components/BlockClockComponent.qml components/BlockCounter.qml components/ConnectionOptions.qml components/ConnectionSettings.qml @@ -9,6 +10,7 @@ components/StorageLocations.qml components/StorageOptions.qml components/StorageSettings.qml + controls/BlockClock.qml controls/ContinueButton.qml controls/ExternalLink.qml controls/Header.qml diff --git a/src/qml/components/BlockClockComponent.qml b/src/qml/components/BlockClockComponent.qml new file mode 100644 index 0000000000..00ed59e4b5 --- /dev/null +++ b/src/qml/components/BlockClockComponent.qml @@ -0,0 +1,61 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import "../controls" + +BlockClock { + id: blockClock + anchors.centerIn: parent + synced: nodeModel.verificationProgress > 0.999 + pause: nodeModel.pause + conns: nodeModel.numOutboundPeers > 0 + + states: [ + State { + name: "intialBlockDownload"; when: !synced && !pause && conns + PropertyChanges { + target: blockClock + + ringProgress: nodeModel.verificationProgress + header: Math.round(ringProgress * 100) + "%" + subText: Math.round(nodeModel.remainingSyncTime/60000) > 0 ? Math.round(nodeModel.remainingSyncTime/60000) + "mins" : Math.round(nodeModel.remainingSyncTime/1000) + "secs" + } + }, + + State { + name: "blockClock"; when: synced && !pause && conns + PropertyChanges { + target: blockClock + + ringProgress: blockList[0] + header: nodeModel.blockTipHeight + subText: "Latest Block" + blockList: chainModel.timeRatioList + } + }, + + State { + name: "Manual Pause"; when: pause + PropertyChanges { + target: blockClock + + ringProgress: 0 + header: "Paused" + subText: "Tap to start" + blockList: {} + } + }, + + State { + name: "Connecting"; when: !pause && !conns + PropertyChanges { + target: blockClock + + ringProgress: 0 + header: "Connecting" + subText: "Please Wait" + blockList: {} + } + } + ] +} diff --git a/src/qml/controls/BlockClock.qml b/src/qml/controls/BlockClock.qml new file mode 100644 index 0000000000..24a3f7b852 --- /dev/null +++ b/src/qml/controls/BlockClock.qml @@ -0,0 +1,157 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import "../components" + +Item { + id: root + + Layout.alignment: Qt.AlignCenter + width: size + height: size + + property real ringProgress: 0 + property string header + property string subText + property bool synced + property bool pause + property bool conns + + property int size: 200 + property real arcBegin: 0 + property real arcEnd: ringProgress * 360 + property real lineWidth: 4 + property string colorCircle: "#f1d54a" + property string colorBackground: Theme.color.neutral2 + + property variant blockList: [] + property variant colorList: ["#EC5445", "#ED6E46", "#EE8847", "#EFA148", "#F0BB49", "#F1D54A"] + + property alias beginAnimation: animationArcBegin.enabled + property alias endAnimation: animationArcEnd.enabled + + property int animationDuration: 250 + + onArcBeginChanged: canvas.requestPaint() + onArcEndChanged: canvas.requestPaint() + onSyncedChanged: canvas.requestPaint() + onBlockListChanged: canvas.requestPaint() + + Behavior on arcBegin { + id: animationArcBegin + enabled: true + NumberAnimation { + duration: root.animationDuration + easing.type: Easing.InOutCubic + } + } + + Behavior on arcEnd { + id: animationArcEnd + enabled: true + NumberAnimation { + easing.type: Easing.Bezier + easing.bezierCurve: [0.5, 0.0, 0.2, 1, 1, 1] + duration: root.animationDuration + } + } + + Canvas { + id: canvas + anchors.fill: parent + rotation: -90 + + onPaint: { + var ctx = getContext("2d") + var x = width / 2 + var y = height / 2 + + ctx.reset() + + // Paint background + ctx.beginPath(); + ctx.arc(x, y, (width / 2) - parent.lineWidth / 2, 0, Math.PI * 2, false) + ctx.lineWidth = root.lineWidth + ctx.strokeStyle = root.colorBackground + ctx.stroke() + + if (!synced) { + var start = Math.PI * (parent.arcBegin / 180) + var end = Math.PI * (parent.arcEnd / 180) + // Paint foreground arc + ctx.beginPath(); + ctx.arc(x, y, (width / 2) - parent.lineWidth / 2, start, end, false) + ctx.lineWidth = root.lineWidth + ctx.strokeStyle = root.colorCircle + ctx.stroke() + } + + else { + var del = 0.0025 + // Paint Block time points + for (var i=1; i 5 ? colorList[5] : colorList[conf] + ctx.stroke() + + // Paint dark segments + var start = Math.PI * ((parent.blockList[i + 1] - del) * 360 / 180) + ctx.beginPath(); + ctx.arc(x, y, (width / 2) - parent.lineWidth/2, start, ends, false) + ctx.lineWidth = 4 + ctx.strokeStyle = root.colorBackground + ctx.stroke(); + } + + // Print last segment + var starts = Math.PI * ((parent.blockList[parent.blockList.length - 1]) * 360 / 180) + var ends = Math.PI * (ringProgress * 360 / 180) + + ctx.beginPath(); + ctx.arc(x, y, (width / 2) - parent.lineWidth / 2, starts, ends, false) + ctx.lineWidth = root.lineWidth + ctx.strokeStyle = colorList[0]; + ctx.stroke() + } + } + } + + ColumnLayout { + anchors.centerIn: root + Image { + Layout.alignment: Qt.AlignCenter + source: "image://images/app" + sourceSize.width: 30 + sourceSize.height: 30 + Layout.bottomMargin: 15 + } + Header { + Layout.fillWidth: true + header: root.header + headerSize: 32 + bold: true + description: root.subText + descriptionMargin: 0 + descriptionColor: Theme.color.neutral4 + Layout.bottomMargin: 20 + } + PeersIndicator { + Layout.alignment: Qt.AlignCenter + numOutboundPeers: nodeModel.numOutboundPeers + maxNumOutboundPeers: nodeModel.maxNumOutboundPeers + } + } + + MouseArea { + anchors.fill: canvas + onClicked: { + pause ? nodeModel.pause = false : nodeModel.pause = true + } + } +} From cd7788eed75c0ca8e3b7b5a1993e70015a40f906 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Wed, 14 Dec 2022 05:02:13 -0500 Subject: [PATCH 16/17] qml: Add the BlockClockComponent to NodeRunner.qml --- src/qml/pages/node/NodeRunner.qml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/qml/pages/node/NodeRunner.qml b/src/qml/pages/node/NodeRunner.qml index 161289dc29..9980e98da9 100644 --- a/src/qml/pages/node/NodeRunner.qml +++ b/src/qml/pages/node/NodeRunner.qml @@ -23,21 +23,7 @@ Page { spacing: 0 anchors.centerIn: parent Component.onCompleted: nodeModel.startNodeInitializionThread(); - Image { - Layout.alignment: Qt.AlignCenter - source: "image://images/app" - sourceSize.width: 64 - sourceSize.height: 64 - } - BlockCounter { - Layout.alignment: Qt.AlignCenter - blockHeight: nodeModel.blockTipHeight - } - ProgressIndicator { - width: 200 - Layout.alignment: Qt.AlignCenter - progress: nodeModel.verificationProgress - } + BlockClockComponent {} } } } From 7d0e8ae0c181b9bcd30d66c51950f9e391703f30 Mon Sep 17 00:00:00 2001 From: Johnny Carlson Date: Thu, 15 Dec 2022 11:45:21 -0500 Subject: [PATCH 17/17] android: remove testnet argument --- src/qt/android/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/android/AndroidManifest.xml b/src/qt/android/AndroidManifest.xml index 41615294e0..fdf081c5b5 100644 --- a/src/qt/android/AndroidManifest.xml +++ b/src/qt/android/AndroidManifest.xml @@ -22,7 +22,7 @@ - +