From 11ba7b400bc7b821f58e636c62d67c7516f4bacf Mon Sep 17 00:00:00 2001 From: jarolrod Date: Fri, 10 Feb 2023 18:46:48 -0700 Subject: [PATCH 01/15] qml: Allow to specify subtext color --- 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 71cb019daa..e9e62d6cae 100644 --- a/src/qml/controls/Header.qml +++ b/src/qml/controls/Header.qml @@ -22,6 +22,7 @@ ColumnLayout { property string subtext: "" property int subtextMargin property int subtextSize: 15 + property color subtextColor: Theme.color.neutral9 property bool wrap: true spacing: 0 @@ -64,7 +65,7 @@ ColumnLayout { font.family: "Inter" font.styleName: "Regular" font.pixelSize: root.subtextSize - color: Theme.color.neutral9 + color: root.subtextColor text: root.subtext horizontalAlignment: root.center ? Text.AlignHCenter : Text.AlignLeft wrapMode: wrap ? Text.WordWrap : Text.NoWrap From 0e8c33e45fa4453501aaca45cba80b7b5a4d4bab Mon Sep 17 00:00:00 2001 From: jarolrod Date: Fri, 10 Feb 2023 18:47:30 -0700 Subject: [PATCH 02/15] qml: Allow to show an error text below Setting header text --- src/qml/controls/Setting.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qml/controls/Setting.qml b/src/qml/controls/Setting.qml index f5ac3bd8dd..df7102d571 100644 --- a/src/qml/controls/Setting.qml +++ b/src/qml/controls/Setting.qml @@ -13,6 +13,8 @@ AbstractButton { property alias actionItem: action_loader.sourceComponent property alias loadedItem: action_loader.item property string description + property string errorText: "" + property bool showErrorText: false property color stateColor hoverEnabled: true state: "FILLED" @@ -83,6 +85,8 @@ AbstractButton { description: root.description descriptionSize: 15 descriptionMargin: 0 + subtext: root.showErrorText ? root.errorText : "" + subtextColor: Theme.color.blue } Loader { id: action_loader From c37d813bc5e78f7ecdd87c1d7d89f5d5d81d4e84 Mon Sep 17 00:00:00 2001 From: Jarol Rodriguez Date: Tue, 21 Feb 2023 23:16:14 -0500 Subject: [PATCH 03/15] qml: Expose minimum and maximum valid Script Verif Threads values --- src/qml/options_model.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/qml/options_model.h b/src/qml/options_model.h index 8b6d942773..2b23b672af 100644 --- a/src/qml/options_model.h +++ b/src/qml/options_model.h @@ -6,6 +6,8 @@ #define BITCOIN_QML_OPTIONS_MODEL_H #include +#include +#include #include @@ -19,6 +21,8 @@ class OptionsQmlModel : public QObject Q_OBJECT Q_PROPERTY(int dbcacheSizeMiB READ dbcacheSizeMiB WRITE setDbcacheSizeMiB NOTIFY dbcacheSizeMiBChanged) Q_PROPERTY(bool listen READ listen WRITE setListen NOTIFY listenChanged) + Q_PROPERTY(int maxScriptThreads READ maxScriptThreads CONSTANT) + Q_PROPERTY(int minScriptThreads READ minScriptThreads CONSTANT) Q_PROPERTY(bool natpmp READ natpmp WRITE setNatpmp NOTIFY natpmpChanged) Q_PROPERTY(bool prune READ prune WRITE setPrune NOTIFY pruneChanged) Q_PROPERTY(int pruneSizeGB READ pruneSizeGB WRITE setPruneSizeGB NOTIFY pruneSizeGBChanged) @@ -33,6 +37,8 @@ class OptionsQmlModel : public QObject void setDbcacheSizeMiB(int new_dbcache_size_mib); bool listen() const { return m_listen; } void setListen(bool new_listen); + int maxScriptThreads() const { return m_max_script_threads; } + int minScriptThreads() const { return m_min_script_threads; } bool natpmp() const { return m_natpmp; } void setNatpmp(bool new_natpmp); bool prune() const { return m_prune; } @@ -62,6 +68,8 @@ class OptionsQmlModel : public QObject // Properties that are exposed to QML. int m_dbcache_size_mib; bool m_listen; + const int m_max_script_threads{MAX_SCRIPTCHECK_THREADS}; + const int m_min_script_threads{-GetNumCores()}; bool m_natpmp; bool m_prune; int m_prune_size_gb; From f5e6562be4b1a020bf96f880a0c3d301dc9a6c6c Mon Sep 17 00:00:00 2001 From: Jarol Rodriguez Date: Tue, 21 Feb 2023 23:28:23 -0500 Subject: [PATCH 04/15] qml: Expose minimum and maximum valid dbcache values --- src/qml/options_model.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/qml/options_model.h b/src/qml/options_model.h index 2b23b672af..6f7788d195 100644 --- a/src/qml/options_model.h +++ b/src/qml/options_model.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_QML_OPTIONS_MODEL_H #define BITCOIN_QML_OPTIONS_MODEL_H +#include #include #include #include @@ -21,6 +22,8 @@ class OptionsQmlModel : public QObject Q_OBJECT Q_PROPERTY(int dbcacheSizeMiB READ dbcacheSizeMiB WRITE setDbcacheSizeMiB NOTIFY dbcacheSizeMiBChanged) Q_PROPERTY(bool listen READ listen WRITE setListen NOTIFY listenChanged) + Q_PROPERTY(int maxDbcacheSizeMiB READ maxDbcacheSizeMiB CONSTANT) + Q_PROPERTY(int minDbcacheSizeMiB READ minDbcacheSizeMiB CONSTANT) Q_PROPERTY(int maxScriptThreads READ maxScriptThreads CONSTANT) Q_PROPERTY(int minScriptThreads READ minScriptThreads CONSTANT) Q_PROPERTY(bool natpmp READ natpmp WRITE setNatpmp NOTIFY natpmpChanged) @@ -37,6 +40,8 @@ class OptionsQmlModel : public QObject void setDbcacheSizeMiB(int new_dbcache_size_mib); bool listen() const { return m_listen; } void setListen(bool new_listen); + int maxDbcacheSizeMiB() const { return m_max_dbcache_size_mib; } + int minDbcacheSizeMiB() const { return m_min_dbcache_size_mib; } int maxScriptThreads() const { return m_max_script_threads; } int minScriptThreads() const { return m_min_script_threads; } bool natpmp() const { return m_natpmp; } @@ -67,6 +72,8 @@ class OptionsQmlModel : public QObject // Properties that are exposed to QML. int m_dbcache_size_mib; + const int m_min_dbcache_size_mib{nMinDbCache}; + const int m_max_dbcache_size_mib{nMaxDbCache}; bool m_listen; const int m_max_script_threads{MAX_SCRIPTCHECK_THREADS}; const int m_min_script_threads{-GetNumCores()}; From cefd4c6349d199c80cc99572d6abb1d48a95a2c6 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Fri, 10 Feb 2023 18:57:24 -0700 Subject: [PATCH 05/15] qml: Validate input for dbcache, threads, and prune target settings Validates that the input for these settings is not less or larger than the allowed values. If so, displays an informational error text. --- src/qml/components/DeveloperOptions.qml | 22 ++++++++++++++++++---- src/qml/components/StorageSettings.qml | 11 +++++++++-- src/qml/controls/ValueInput.qml | 10 ++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/qml/components/DeveloperOptions.qml b/src/qml/components/DeveloperOptions.qml index c693d96051..59e47565cf 100644 --- a/src/qml/components/DeveloperOptions.qml +++ b/src/qml/components/DeveloperOptions.qml @@ -27,12 +27,19 @@ ColumnLayout { id: dbcacheSetting Layout.fillWidth: true header: qsTr("Database cache size (MiB)") + errorText: qsTr("This is not a valid cache size. Please choose a value between %1 and %2 MiB.").arg(optionsModel.minDbcacheSizeMiB).arg(optionsModel.maxDbcacheSizeMiB) + showErrorText: false actionItem: ValueInput { parentState: dbcacheSetting.state description: optionsModel.dbcacheSizeMiB onEditingFinished: { - optionsModel.dbcacheSizeMiB = parseInt(text) - dbcacheSetting.forceActiveFocus() + if (checkValidity(optionsModel.minDbcacheSizeMiB, optionsModel.maxDbcacheSizeMiB, parseInt(text))) { + optionsModel.dbcacheSizeMiB = parseInt(text) + dbcacheSetting.forceActiveFocus() + dbcacheSetting.showErrorText = false + } else { + dbcacheSetting.showErrorText = true + } } } onClicked: { @@ -45,12 +52,19 @@ ColumnLayout { id: parSetting Layout.fillWidth: true header: qsTr("Script verification threads") + errorText: qsTr("This is not a valid thread count. Please choose a value between %1 and %2 threads.").arg(optionsModel.minScriptThreads).arg(optionsModel.maxScriptThreads) + showErrorText: !loadedItem.acceptableInput && loadedItem.length > 0 actionItem: ValueInput { parentState: parSetting.state description: optionsModel.scriptThreads onEditingFinished: { - optionsModel.scriptThreads = parseInt(text) - parSetting.forceActiveFocus() + if (checkValidity(optionsModel.minScriptThreads, optionsModel.maxScriptThreads, parseInt(text))) { + optionsModel.scriptThreads = parseInt(text) + parSetting.forceActiveFocus() + parSetting.showErrorText = false + } else { + parSetting.showErrorText = true + } } } onClicked: { diff --git a/src/qml/components/StorageSettings.qml b/src/qml/components/StorageSettings.qml index d00355ec6a..55cdaf246d 100644 --- a/src/qml/components/StorageSettings.qml +++ b/src/qml/components/StorageSettings.qml @@ -33,12 +33,19 @@ ColumnLayout { id: pruneTargetSetting Layout.fillWidth: true header: qsTr("Storage limit (GB)") + errorText: qsTr("This is not a valid prune target. Please choose a value that is equal to or larger than 1GB") + showErrorText: false actionItem: ValueInput { parentState: pruneTargetSetting.state description: optionsModel.pruneSizeGB onEditingFinished: { - optionsModel.pruneSizeGB = parseInt(text) - pruneTargetSetting.forceActiveFocus() + if (parseInt(text) < 1) { + pruneTargetSetting.showErrorText = true + } else { + optionsModel.pruneSizeGB = parseInt(text) + pruneTargetSetting.forceActiveFocus() + pruneTargetSetting.showErrorText = false + } } } onClicked: { diff --git a/src/qml/controls/ValueInput.qml b/src/qml/controls/ValueInput.qml index cf92d183d3..2d550b223a 100644 --- a/src/qml/controls/ValueInput.qml +++ b/src/qml/controls/ValueInput.qml @@ -14,6 +14,8 @@ TextInput { property color textColor: root.filled ? Theme.color.neutral9 : Theme.color.neutral5 enabled: true state: root.parentState + validator: IntValidator{} + maximumLength: 5 states: [ State { @@ -48,4 +50,12 @@ TextInput { Behavior on color { ColorAnimation { duration: 150 } } + + function checkValidity(minVal, maxVal, input) { + if (input < minVal || input > maxVal) { + return false + } else { + return true + } + } } From 2b138435afa47b2931f76b9d712e3e2e82aad175 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Wed, 1 Feb 2023 22:17:11 -0500 Subject: [PATCH 06/15] qml: move setting of default prune setting into StorageOptions --- src/qml/components/StorageOptions.qml | 4 ++++ src/qml/pages/settings/SettingsStorage.qml | 8 +------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/qml/components/StorageOptions.qml b/src/qml/components/StorageOptions.qml index b8e42a5146..ddb0b8a7a3 100644 --- a/src/qml/components/StorageOptions.qml +++ b/src/qml/components/StorageOptions.qml @@ -23,6 +23,10 @@ ColumnLayout { optionsModel.prune = true optionsModel.pruneSizeGB = 2 } + Component.onCompleted: { + optionsModel.prune = true + optionsModel.pruneSizeGB = 2 + } } OptionButton { Layout.fillWidth: true diff --git a/src/qml/pages/settings/SettingsStorage.qml b/src/qml/pages/settings/SettingsStorage.qml index 14233059a8..3c880b416c 100644 --- a/src/qml/pages/settings/SettingsStorage.qml +++ b/src/qml/pages/settings/SettingsStorage.qml @@ -14,11 +14,5 @@ InformationPage { headerText: qsTr("Storage settings") headerMargin: 0 detailActive: true - detailItem: StorageSettings { - // Set default prune values - Component.onCompleted: { - optionsModel.prune = true - optionsModel.pruneSizeGB = 2 - } - } + detailItem: StorageSettings {} } From 205d2471a843987754e84bc1949d42ccaaf917ea Mon Sep 17 00:00:00 2001 From: jarolrod Date: Wed, 1 Feb 2023 22:17:41 -0500 Subject: [PATCH 07/15] qml: expose currently loaded detailItem in InformationPage --- src/qml/controls/InformationPage.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/controls/InformationPage.qml b/src/qml/controls/InformationPage.qml index 600faefcc8..3f2bd052db 100644 --- a/src/qml/controls/InformationPage.qml +++ b/src/qml/controls/InformationPage.qml @@ -12,6 +12,7 @@ Page { implicitHeight: parent.height property alias bannerItem: banner_loader.sourceComponent property alias detailItem: detail_loader.sourceComponent + property alias loadedDetailItem: detail_loader.item property alias navLeftDetail: navbar.leftDetail property alias navMiddleDetail: navbar.middleDetail property alias navRightDetail: navbar.rightDetail From fa433b1f69372039a03142e664a6c88fe7c6e2c2 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Wed, 1 Feb 2023 22:20:49 -0500 Subject: [PATCH 08/15] qml: Introduce Storage OptionButton that represents custom prune target This allows for a new OptionButton to be shown in StorageOptions when the user has going into detailed settings during onboarding and chosen a custom prune target --- src/qml/components/StorageOptions.qml | 21 ++++++++++++++++++- src/qml/components/StorageSettings.qml | 5 +++++ .../onboarding/OnboardingStorageAmount.qml | 3 +++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/qml/components/StorageOptions.qml b/src/qml/components/StorageOptions.qml index ddb0b8a7a3..1f4b5553e3 100644 --- a/src/qml/components/StorageOptions.qml +++ b/src/qml/components/StorageOptions.qml @@ -8,6 +8,9 @@ import QtQuick.Layouts 1.15 import "../controls" ColumnLayout { + id: root + property bool customStorage: false + property int customStorageAmount ButtonGroup { id: group } @@ -18,7 +21,7 @@ ColumnLayout { text: qsTr("Reduce storage") description: qsTr("Uses about 2GB. For simple wallet use.") recommended: true - checked: true + checked: !root.customStorage && optionsModel.prune onClicked: { optionsModel.prune = true optionsModel.pruneSizeGB = 2 @@ -32,9 +35,25 @@ ColumnLayout { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Store all data") + checked: !optionsModel.prune description: qsTr("Uses about 550GB. Support the network.") onClicked: { optionsModel.prune = false } } + Loader { + Layout.fillWidth: true + active: root.customStorage + visible: active + sourceComponent: OptionButton { + ButtonGroup.group: group + checked: root.customStorage && optionsModel.prune + text: qsTr("Custom") + description: qsTr("Storing recent blocks up to %1GB").arg(root.customStorageAmount) + onClicked: { + optionsModel.prune = true + optionsModel.pruneSizeGB = root.customStorageAmount + } + } + } } diff --git a/src/qml/components/StorageSettings.qml b/src/qml/components/StorageSettings.qml index 55cdaf246d..bab3160f72 100644 --- a/src/qml/components/StorageSettings.qml +++ b/src/qml/components/StorageSettings.qml @@ -8,6 +8,9 @@ import QtQuick.Layouts 1.15 import "../controls" ColumnLayout { + id: root + property bool customStorage: false + property int customStorageAmount spacing: 4 Setting { Layout.fillWidth: true @@ -42,6 +45,8 @@ ColumnLayout { if (parseInt(text) < 1) { pruneTargetSetting.showErrorText = true } else { + root.customStorage = true + root.customStorageAmount = parseInt(text) optionsModel.pruneSizeGB = parseInt(text) pruneTargetSetting.forceActiveFocus() pruneTargetSetting.showErrorText = false diff --git a/src/qml/pages/onboarding/OnboardingStorageAmount.qml b/src/qml/pages/onboarding/OnboardingStorageAmount.qml index bb4d913dc1..e4cdf06e20 100644 --- a/src/qml/pages/onboarding/OnboardingStorageAmount.qml +++ b/src/qml/pages/onboarding/OnboardingStorageAmount.qml @@ -33,6 +33,8 @@ Page { detailItem: ColumnLayout { spacing: 0 StorageOptions { + customStorage: advancedStorage.loadedDetailItem.customStorage + customStorageAmount: advancedStorage.loadedDetailItem.customStorageAmount Layout.maximumWidth: 450 Layout.alignment: Qt.AlignCenter } @@ -47,6 +49,7 @@ Page { buttonMargin: 20 } SettingsStorage { + id: advancedStorage navRightDetail: NavButton { text: qsTr("Done") onClicked: { From e8fa39a965089da6d1798ec91e4be5fe20cbff4b Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Sat, 4 Feb 2023 20:28:51 -0500 Subject: [PATCH 09/15] qml: only show Onboarding if settings/conf file is missing --- src/qml/bitcoin.cpp | 36 +++++++++++++++++++++++++++++++++++- src/qml/pages/main.qml | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 8506c3ece3..b085ea9618 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -92,6 +92,26 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons LogPrintf("GUI: %s\n", msg.toStdString()); } } + +bool ConfigurationFileExists(ArgsManager& argsman) +{ + fs::path settings_path; + if (!argsman.GetSettingsPath(&settings_path)) { + // settings file is disabled + return true; + } + if (fs::exists(settings_path)) { + return true; + } + + const fs::path rel_config_path = argsman.GetPathArg("-conf", BITCOIN_CONF_FILENAME); + const fs::path abs_config_path = AbsPathForConfigVal(rel_config_path, true); + if (fs::exists(abs_config_path)) { + return true; + } + + return false; +} } // namespace @@ -146,11 +166,24 @@ int QmlGuiMain(int argc, char* argv[]) } /// Read and parse settings.json file. - if (!gArgs.InitSettings(error)) { + std::vector errors; + if (!gArgs.ReadSettingsFile(&errors)) { + error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors)); InitError(Untranslated(error)); return EXIT_FAILURE; } + QVariant need_onboarding(true); + if (gArgs.IsArgSet("-datadir") && !gArgs.GetPathArg("-datadir").empty()) { + need_onboarding.setValue(false); + } else if (ConfigurationFileExists(gArgs)) { + need_onboarding.setValue(false); + } + + if (gArgs.IsArgSet("-resetguisettings")) { + need_onboarding.setValue(true); + } + // Default printtoconsole to false for the GUI. GUI programs should not // print to the console unnecessarily. gArgs.SoftSetBoolArg("-printtoconsole", false); @@ -208,6 +241,7 @@ int QmlGuiMain(int argc, char* argv[]) OptionsQmlModel options_model{*node}; engine.rootContext()->setContextProperty("optionsModel", &options_model); + engine.rootContext()->setContextProperty("needOnboarding", need_onboarding); #ifdef __ANDROID__ AppMode app_mode(AppMode::MOBILE); #else diff --git a/src/qml/pages/main.qml b/src/qml/pages/main.qml index 72a01a5ef2..ad53097a9d 100644 --- a/src/qml/pages/main.qml +++ b/src/qml/pages/main.qml @@ -21,7 +21,7 @@ ApplicationWindow { StackView { id: main - initialItem: onboardingWizard + initialItem: needOnboarding ? onboardingWizard : node anchors.fill: parent } From f37cae718bf40801e378da547a448cfc5029af77 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Tue, 28 Feb 2023 02:10:58 -0500 Subject: [PATCH 10/15] qml: format BlockClock progress percentage according to the design file --- src/qml/components/BlockClock.qml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/qml/components/BlockClock.qml b/src/qml/components/BlockClock.qml index 71ab1d97cb..ec42b21f45 100644 --- a/src/qml/components/BlockClock.qml +++ b/src/qml/components/BlockClock.qml @@ -93,7 +93,7 @@ Item { name: "IBD"; when: !synced && !paused && connected PropertyChanges { target: root - header: Math.round(nodeModel.verificationProgress * 100) + "%" + header: formatProgressPercentage(nodeModel.verificationProgress * 100) subText: formatRemainingSyncTime(nodeModel.remainingSyncTime) } }, @@ -144,6 +144,18 @@ Item { } ] + function formatProgressPercentage(progress) { + if (progress >= 1) { + return Math.round(progress) + "%" + } else if (progress >= 0.1) { + return progress.toFixed(1) + "%" + } else if (progress >= 0.01) { + return progress.toFixed(2) + "%" + } else { + return "0%" + } + } + function formatRemainingSyncTime(milliseconds) { var minutes = Math.floor(milliseconds / 60000); var seconds = Math.floor((milliseconds % 60000) / 1000); From 18aa5c95fcd3c5713399e0b2470cd23cd00060d6 Mon Sep 17 00:00:00 2001 From: Jarol Rodriguez Date: Wed, 1 Mar 2023 16:28:33 -0500 Subject: [PATCH 11/15] qml: expose assumed blockchain and chainstate size as to qml --- src/qml/chainmodel.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/qml/chainmodel.h b/src/qml/chainmodel.h index c25d8d11a1..91e2ddc1dd 100644 --- a/src/qml/chainmodel.h +++ b/src/qml/chainmodel.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_QML_CHAINMODEL_H #define BITCOIN_QML_CHAINMODEL_H +#include #include #include @@ -23,6 +24,8 @@ class ChainModel : public QObject { Q_OBJECT Q_PROPERTY(QString currentNetworkName READ currentNetworkName WRITE setCurrentNetworkName NOTIFY currentNetworkNameChanged) + Q_PROPERTY(quint64 assumedBlockchainSize READ assumedBlockchainSize CONSTANT) + Q_PROPERTY(quint64 assumedChainstateSize READ assumedChainstateSize CONSTANT) Q_PROPERTY(QVariantList timeRatioList READ timeRatioList NOTIFY timeRatioListChanged) public: @@ -30,6 +33,8 @@ class ChainModel : public QObject QString currentNetworkName() const { return m_current_network_name; }; void setCurrentNetworkName(QString network_name); + quint64 assumedBlockchainSize() const { return m_assumed_blockchain_size; }; + quint64 assumedChainstateSize() const { return m_assumed_chainstate_size; }; QVariantList timeRatioList() const { return m_time_ratio_list; }; int timestampAtMeridian(); @@ -46,6 +51,8 @@ public Q_SLOTS: private: QString m_current_network_name; + quint64 m_assumed_blockchain_size{ Params().AssumedBlockchainSize() }; + quint64 m_assumed_chainstate_size{ Params().AssumedChainStateSize() }; /* time_ratio: Ratio between the time at which an event * happened and 12 hours. So, for example, if a block is * found at 4 am or pm, the time_ratio would be 0.3. From 67a220ce330c9b9f7a1098ae8b0a86b57862095e Mon Sep 17 00:00:00 2001 From: Jarol Rodriguez Date: Wed, 1 Mar 2023 16:29:32 -0500 Subject: [PATCH 12/15] qml: smart storage values based on assumed chain and chainstate size --- src/qml/components/StorageOptions.qml | 10 +++++++--- src/qml/components/StorageSettings.qml | 2 +- src/qml/pages/onboarding/OnboardingStorageLocation.qml | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/qml/components/StorageOptions.qml b/src/qml/components/StorageOptions.qml index 1f4b5553e3..eed962b951 100644 --- a/src/qml/components/StorageOptions.qml +++ b/src/qml/components/StorageOptions.qml @@ -5,6 +5,9 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 + +import org.bitcoincore.qt 1.0 + import "../controls" ColumnLayout { @@ -19,7 +22,7 @@ ColumnLayout { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Reduce storage") - description: qsTr("Uses about 2GB. For simple wallet use.") + description: qsTr("Uses about %1GB. For simple wallet use.").arg(chainModel.assumedChainstateSize + 2) recommended: true checked: !root.customStorage && optionsModel.prune onClicked: { @@ -36,7 +39,8 @@ ColumnLayout { ButtonGroup.group: group text: qsTr("Store all data") checked: !optionsModel.prune - description: qsTr("Uses about 550GB. Support the network.") + description: qsTr("Uses about %1GB. Support the network.").arg( + chainModel.assumedBlockchainSize + chainModel.assumedChainstateSize) onClicked: { optionsModel.prune = false } @@ -49,7 +53,7 @@ ColumnLayout { ButtonGroup.group: group checked: root.customStorage && optionsModel.prune text: qsTr("Custom") - description: qsTr("Storing recent blocks up to %1GB").arg(root.customStorageAmount) + description: qsTr("Storing about %1GB of data.").arg(root.customStorageAmount + chainModel.assumedChainstateSize) onClicked: { optionsModel.prune = true optionsModel.pruneSizeGB = root.customStorageAmount diff --git a/src/qml/components/StorageSettings.qml b/src/qml/components/StorageSettings.qml index bab3160f72..2a2951bfac 100644 --- a/src/qml/components/StorageSettings.qml +++ b/src/qml/components/StorageSettings.qml @@ -35,7 +35,7 @@ ColumnLayout { Setting { id: pruneTargetSetting Layout.fillWidth: true - header: qsTr("Storage limit (GB)") + header: qsTr("Block Storage limit (GB)") errorText: qsTr("This is not a valid prune target. Please choose a value that is equal to or larger than 1GB") showErrorText: false actionItem: ValueInput { diff --git a/src/qml/pages/onboarding/OnboardingStorageLocation.qml b/src/qml/pages/onboarding/OnboardingStorageLocation.qml index b71937d1c1..b2c3aa1e00 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?\nYou need a minimum of 1GB of storage.") + description: qsTr("Where do you want to store the downloaded block data?\nYou need a minimum of %1GB of storage.").arg(chainModel.assumedChainstateSize + 1) descriptionMargin: 20 detailActive: true detailItem: StorageLocations {} From 9709f8e740a40ba3969a911cfaced4d3dfc8f122 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Wed, 1 Mar 2023 21:49:50 -0500 Subject: [PATCH 13/15] qml: consistent color animation --- src/qml/components/PeersIndicator.qml | 4 ++++ src/qml/components/Separator.qml | 4 ++++ src/qml/controls/CoreText.qml | 4 ++++ src/qml/controls/Header.qml | 8 ++++++++ src/qml/controls/NavButton.qml | 4 ++++ src/qml/controls/OptionButton.qml | 12 ++++++++++++ src/qml/controls/OptionSwitch.qml | 8 ++++++++ src/qml/pages/main.qml | 4 ++++ 8 files changed, 48 insertions(+) diff --git a/src/qml/components/PeersIndicator.qml b/src/qml/components/PeersIndicator.qml index d0f739b9eb..e31f349f4c 100644 --- a/src/qml/components/PeersIndicator.qml +++ b/src/qml/components/PeersIndicator.qml @@ -28,6 +28,10 @@ RowLayout { SmoothedAnimation { to: 0; velocity: 2.2 } SmoothedAnimation { to: 1; velocity: 2.2 } } + + Behavior on color { + ColorAnimation { duration: 150 } + } } } } diff --git a/src/qml/components/Separator.qml b/src/qml/components/Separator.qml index c5ca656aec..869308568f 100644 --- a/src/qml/components/Separator.qml +++ b/src/qml/components/Separator.qml @@ -9,4 +9,8 @@ import "../controls" Rectangle { height: 1 color: Theme.color.neutral5 + + Behavior on color { + ColorAnimation { duration: 150 } + } } diff --git a/src/qml/controls/CoreText.qml b/src/qml/controls/CoreText.qml index 8e10272bbe..050f03b256 100644 --- a/src/qml/controls/CoreText.qml +++ b/src/qml/controls/CoreText.qml @@ -15,4 +15,8 @@ Text { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: wrap ? Text.WordWrap : Text.NoWrap + + Behavior on color { + ColorAnimation { duration: 150 } + } } diff --git a/src/qml/controls/Header.qml b/src/qml/controls/Header.qml index e9e62d6cae..58d2835fe2 100644 --- a/src/qml/controls/Header.qml +++ b/src/qml/controls/Header.qml @@ -54,6 +54,10 @@ ColumnLayout { text: root.description horizontalAlignment: root.center ? Text.AlignHCenter : Text.AlignLeft wrapMode: wrap ? Text.WordWrap : Text.NoWrap + + Behavior on color { + ColorAnimation { duration: 150 } + } } } Loader { @@ -69,6 +73,10 @@ ColumnLayout { text: root.subtext horizontalAlignment: root.center ? Text.AlignHCenter : Text.AlignLeft wrapMode: wrap ? Text.WordWrap : Text.NoWrap + + Behavior on color { + ColorAnimation { duration: 150 } + } } } } diff --git a/src/qml/controls/NavButton.qml b/src/qml/controls/NavButton.qml index bf2fe051df..d9741bdd8e 100644 --- a/src/qml/controls/NavButton.qml +++ b/src/qml/controls/NavButton.qml @@ -66,6 +66,10 @@ AbstractButton { icon.height: root.iconHeight icon.width: root.iconWidth background: root.iconBackground + + Behavior on icon.color { + ColorAnimation { duration: 150 } + } } } Loader { diff --git a/src/qml/controls/OptionButton.qml b/src/qml/controls/OptionButton.qml index fc3022c6a7..f11ddf07a3 100644 --- a/src/qml/controls/OptionButton.qml +++ b/src/qml/controls/OptionButton.qml @@ -47,6 +47,10 @@ Button { background: Rectangle { color: Theme.color.neutral9 radius: 3 + + Behavior on color { + ColorAnimation { duration: 150 } + } } font.styleName: "Regular" font.pixelSize: 13 @@ -56,6 +60,10 @@ Button { leftPadding: 7 color: Theme.color.neutral0 text: qsTr("Recommended") + + Behavior on color { + ColorAnimation { duration: 150 } + } } } } @@ -70,6 +78,10 @@ Button { icon.height: 24 icon.width: 24 background: null + + Behavior on icon.color { + ColorAnimation { duration: 150 } + } } } } diff --git a/src/qml/controls/OptionSwitch.qml b/src/qml/controls/OptionSwitch.qml index 32d09af2fd..9a9ec32442 100644 --- a/src/qml/controls/OptionSwitch.qml +++ b/src/qml/controls/OptionSwitch.qml @@ -12,6 +12,10 @@ Switch { background: Rectangle { radius: Math.floor(height / 2) color: root.checked ? Theme.color.orange : Theme.color.neutral4 + + Behavior on color { + ColorAnimation { duration: 150 } + } } indicator: Rectangle { property real _margin: Math.round((parent.height - height) / 2) @@ -25,5 +29,9 @@ Switch { SmoothedAnimation { } } + + Behavior on color { + ColorAnimation { duration: 150 } + } } } diff --git a/src/qml/pages/main.qml b/src/qml/pages/main.qml index ad53097a9d..7f528ac4ea 100644 --- a/src/qml/pages/main.qml +++ b/src/qml/pages/main.qml @@ -19,6 +19,10 @@ ApplicationWindow { color: Theme.color.background visible: true + Behavior on color { + ColorAnimation { duration: 150 } + } + StackView { id: main initialItem: needOnboarding ? onboardingWizard : node From df524c343527042a94a23a8fcfdc5fc5f47456a6 Mon Sep 17 00:00:00 2001 From: jarolrod Date: Mon, 13 Feb 2023 01:44:15 -0700 Subject: [PATCH 14/15] qml: Add connection animation to block clock --- src/qml/components/BlockClock.qml | 1 + src/qml/components/blockclockdial.cpp | 51 +++++++++++++++++++++++++-- src/qml/components/blockclockdial.h | 11 ++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/qml/components/BlockClock.qml b/src/qml/components/BlockClock.qml index ec42b21f45..a12bb1da6f 100644 --- a/src/qml/components/BlockClock.qml +++ b/src/qml/components/BlockClock.qml @@ -32,6 +32,7 @@ Item { timeRatioList: chainModel.timeRatioList verificationProgress: nodeModel.verificationProgress paused: root.paused + connected: root.connected synced: nodeModel.verificationProgress > 0.999 backgroundColor: Theme.color.neutral2 timeTickColor: Theme.color.neutral5 diff --git a/src/qml/components/blockclockdial.cpp b/src/qml/components/blockclockdial.cpp index e451eb5642..cb0bf49ed2 100644 --- a/src/qml/components/blockclockdial.cpp +++ b/src/qml/components/blockclockdial.cpp @@ -4,8 +4,10 @@ #include +#include #include #include +#include #include #include @@ -18,6 +20,26 @@ BlockClockDial::BlockClockDial(QQuickItem *parent) { } +void BlockClockDial::setupConnectingGradient(const QPen & pen) +{ + m_connecting_gradient.setCenter(getBoundsForPen(pen).center()); + m_connecting_gradient.setAngle(m_connecting_start_angle); + m_connecting_gradient.setColorAt(0, m_confirmation_colors[5]); + m_connecting_gradient.setColorAt(0.5, m_confirmation_colors[5]); + m_connecting_gradient.setColorAt(0.6, m_confirmation_colors[4]); + m_connecting_gradient.setColorAt(0.7, m_confirmation_colors[3]); + m_connecting_gradient.setColorAt(1, "transparent"); +} + +qreal BlockClockDial::decrementGradientAngle(qreal angle) +{ + if (angle == -360) { + return 0; + } else { + return angle -= 4; + } +} + void BlockClockDial::setTimeRatioList(QVariantList new_list) { m_time_ratio_list = new_list; @@ -30,6 +52,12 @@ void BlockClockDial::setVerificationProgress(double progress) update(); } +void BlockClockDial::setConnected(bool connected) +{ + m_is_connected = connected; + update(); +} + void BlockClockDial::setSynced(bool synced) { m_is_synced = synced; @@ -139,6 +167,20 @@ void BlockClockDial::paintProgress(QPainter * painter) painter->drawArc(bounds, startAngle * 16, spanAngle * 16); } +void BlockClockDial::paintConnectingAnimation(QPainter * painter) +{ + QPen pen; + pen.setWidthF(4); + setupConnectingGradient(pen); + pen.setBrush(QBrush(m_connecting_gradient)); + pen.setCapStyle(Qt::RoundCap); + const QRectF bounds = getBoundsForPen(pen); + painter->setPen(pen); + painter->drawArc(bounds, m_connecting_start_angle * 16, m_connecting_end_angle * 16); + m_connecting_start_angle = decrementGradientAngle(m_connecting_start_angle); + update(); +} + void BlockClockDial::paintBackground(QPainter * painter) { QPen pen(m_background_color); @@ -186,9 +228,14 @@ void BlockClockDial::paint(QPainter * painter) if (paused()) return; - if (synced()) { + if (!(connected())) { + paintConnectingAnimation(painter); + return; + } + + if (synced() && connected()) { paintBlocks(painter); - } else { + } else if (!synced() && connected()) { paintProgress(painter); } } diff --git a/src/qml/components/blockclockdial.h b/src/qml/components/blockclockdial.h index 408fac618b..caff7f894a 100644 --- a/src/qml/components/blockclockdial.h +++ b/src/qml/components/blockclockdial.h @@ -6,6 +6,7 @@ #define BITCOIN_QML_COMPONENTS_BLOCKCLOCKDIAL_H #include +#include #include class BlockClockDial : public QQuickPaintedItem @@ -13,6 +14,7 @@ class BlockClockDial : public QQuickPaintedItem Q_OBJECT Q_PROPERTY(QVariantList timeRatioList READ timeRatioList WRITE setTimeRatioList) Q_PROPERTY(double verificationProgress READ verificationProgress WRITE setVerificationProgress) + Q_PROPERTY(bool connected READ connected WRITE setConnected) Q_PROPERTY(bool synced READ synced WRITE setSynced) Q_PROPERTY(bool paused READ paused WRITE setPaused) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) @@ -25,6 +27,7 @@ class BlockClockDial : public QQuickPaintedItem QVariantList timeRatioList() const { return m_time_ratio_list; }; double verificationProgress() const { return m_verification_progress; }; + bool connected() const { return m_is_connected; }; bool synced() const { return m_is_synced; }; bool paused() const { return m_is_paused; }; QColor backgroundColor() const { return m_background_color; }; @@ -34,6 +37,7 @@ class BlockClockDial : public QQuickPaintedItem public Q_SLOTS: void setTimeRatioList(QVariantList new_time); void setVerificationProgress(double progress); + void setConnected(bool connected); void setSynced(bool synced); void setPaused(bool paused); void setBackgroundColor(QColor color); @@ -41,18 +45,25 @@ public Q_SLOTS: void setTimeTickColor(QColor color); private: + void paintConnectingAnimation(QPainter * painter); void paintProgress(QPainter * painter); void paintBlocks(QPainter * painter); void paintBackground(QPainter * painter); void paintTimeTicks(QPainter * painter); QRectF getBoundsForPen(const QPen & pen); double degreesPerPixel(); + void setupConnectingGradient(const QPen & pen); + qreal decrementGradientAngle(qreal angle); QVariantList m_time_ratio_list; double m_verification_progress; + bool m_is_connected; bool m_is_synced; bool m_is_paused; QColor m_background_color; + QConicalGradient m_connecting_gradient; + qreal m_connecting_start_angle = 90; + const qreal m_connecting_end_angle = -180; QList m_confirmation_colors; QColor m_time_tick_color; }; From 3ee73809eb4b8e0d6526b7ed333a1f570fd97892 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Mon, 20 Feb 2023 00:04:15 -0500 Subject: [PATCH 15/15] qml: Add timer to control BlockClockDial connecting animation --- src/qml/components/blockclockdial.cpp | 25 ++++++++++++++++++++++--- src/qml/components/blockclockdial.h | 3 +++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/qml/components/blockclockdial.cpp b/src/qml/components/blockclockdial.cpp index cb0bf49ed2..3673649d9a 100644 --- a/src/qml/components/blockclockdial.cpp +++ b/src/qml/components/blockclockdial.cpp @@ -17,7 +17,19 @@ BlockClockDial::BlockClockDial(QQuickItem *parent) , m_background_color{QColor("#2D2D2D")} , m_confirmation_colors{QList{}} , m_time_tick_color{QColor("#000000")} +, m_animation_timer{QTimer(this)} +, m_delay_timer{QTimer(this)} { + m_animation_timer.setTimerType(Qt::PreciseTimer); + m_animation_timer.setInterval(16); + m_delay_timer.setTimerType(Qt::PreciseTimer); + m_delay_timer.setSingleShot(true); + m_delay_timer.setInterval(5000); + connect(&m_animation_timer, &QTimer::timeout, + this, [=]() { this->update(); }); + connect(&m_delay_timer, &QTimer::timeout, + this, [=]() { this->m_animation_timer.start(); }); + m_delay_timer.start(); } void BlockClockDial::setupConnectingGradient(const QPen & pen) @@ -54,7 +66,15 @@ void BlockClockDial::setVerificationProgress(double progress) void BlockClockDial::setConnected(bool connected) { - m_is_connected = connected; + if (m_is_connected != connected) { + m_is_connected = connected; + m_delay_timer.stop(); + if (m_is_connected) { + m_animation_timer.stop(); + } else { + m_delay_timer.start(); + } + } update(); } @@ -178,7 +198,6 @@ void BlockClockDial::paintConnectingAnimation(QPainter * painter) painter->setPen(pen); painter->drawArc(bounds, m_connecting_start_angle * 16, m_connecting_end_angle * 16); m_connecting_start_angle = decrementGradientAngle(m_connecting_start_angle); - update(); } void BlockClockDial::paintBackground(QPainter * painter) @@ -228,7 +247,7 @@ void BlockClockDial::paint(QPainter * painter) if (paused()) return; - if (!(connected())) { + if (m_animation_timer.isActive()) { paintConnectingAnimation(painter); return; } diff --git a/src/qml/components/blockclockdial.h b/src/qml/components/blockclockdial.h index caff7f894a..30a7ba8759 100644 --- a/src/qml/components/blockclockdial.h +++ b/src/qml/components/blockclockdial.h @@ -8,6 +8,7 @@ #include #include #include +#include class BlockClockDial : public QQuickPaintedItem { @@ -66,6 +67,8 @@ public Q_SLOTS: const qreal m_connecting_end_angle = -180; QList m_confirmation_colors; QColor m_time_tick_color; + QTimer m_animation_timer; + QTimer m_delay_timer; }; #endif // BITCOIN_QML_COMPONENTS_BLOCKCLOCKDIAL_H