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/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 diff --git a/src/qml/controls/Setting.qml b/src/qml/controls/Setting.qml index 7415d6add9..122bb71cc7 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" @@ -75,6 +77,8 @@ AbstractButton { description: root.description descriptionSize: 15 descriptionMargin: 0 + subtext: root.showErrorText ? root.errorText : "" + subtextColor: Theme.color.blue } Loader { id: action_loader 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 + } + } } diff --git a/src/qml/options_model.h b/src/qml/options_model.h index 8b6d942773..6f7788d195 100644 --- a/src/qml/options_model.h +++ b/src/qml/options_model.h @@ -5,7 +5,10 @@ #ifndef BITCOIN_QML_OPTIONS_MODEL_H #define BITCOIN_QML_OPTIONS_MODEL_H +#include #include +#include +#include #include @@ -19,6 +22,10 @@ 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) Q_PROPERTY(bool prune READ prune WRITE setPrune NOTIFY pruneChanged) Q_PROPERTY(int pruneSizeGB READ pruneSizeGB WRITE setPruneSizeGB NOTIFY pruneSizeGBChanged) @@ -33,6 +40,10 @@ 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; } void setNatpmp(bool new_natpmp); bool prune() const { return m_prune; } @@ -61,7 +72,11 @@ 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()}; bool m_natpmp; bool m_prune; int m_prune_size_gb;