From 1746e24dd9225c6ab0862db4a0b9b1ef7aa6aaf9 Mon Sep 17 00:00:00 2001 From: casper Date: Mon, 7 Feb 2022 19:59:57 +0100 Subject: [PATCH 1/6] Make `SpinBox` a reusable component Forgot to update Readonly `SpinBox`es to Qt 2 when implementing Cura 8684. As the spinbox is used in various places with the same functionality it made sense to create a reusable component. There is still a feature gap in the implementation; The `SpinBox`es from QtControls 2.x value is defined as an integer. For some use-cases we do require a fractional value in the `SpinBox`. The only two places where this is required are the `material_diameter` and `material_density` fields in the material prefference page. Cura 8684 --- .../Preferences/Materials/MaterialsView.qml | 67 +++++-------------- resources/qml/Preferences/ReadOnlySpinBox.qml | 54 --------------- resources/qml/SpinBox.qml | 47 +++++++++++++ 3 files changed, 63 insertions(+), 105 deletions(-) delete mode 100644 resources/qml/Preferences/ReadOnlySpinBox.qml create mode 100644 resources/qml/SpinBox.qml diff --git a/resources/qml/Preferences/Materials/MaterialsView.qml b/resources/qml/Preferences/Materials/MaterialsView.qml index d1ea251ab8..283d7a84d2 100644 --- a/resources/qml/Preferences/Materials/MaterialsView.qml +++ b/resources/qml/Preferences/Materials/MaterialsView.qml @@ -234,30 +234,31 @@ Item Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") } Label { width: informationPage.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } - ReadOnlySpinBox + + Cura.SpinBox { + enabled: base.editingEnabled id: densitySpinBox width: informationPage.columnWidth value: properties.density decimals: 2 suffix: " g/cm³" - stepSize: 0.01 - readOnly: !base.editingEnabled + stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value) onValueChanged: updateCostPerMeter() } Label { width: informationPage.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } - ReadOnlySpinBox + Cura.SpinBox { + enabled: base.editingEnabled id: diameterSpinBox width: informationPage.columnWidth value: properties.diameter decimals: 2 suffix: " mm" - stepSize: 0.01 - readOnly: !base.editingEnabled + stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size onEditingFinished: { @@ -283,44 +284,26 @@ Item } Label { width: informationPage.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } - SpinBox + + Cura.SpinBox { id: spoolCostSpinBox width: informationPage.columnWidth value: base.getMaterialPreferenceValue(properties.guid, "spool_cost") to: 100000000 editable: true - - contentItem: TextField - { - text: spoolCostSpinBox.textFromValue(spoolCostSpinBox.value, spoolCostSpinBox.locale) - selectByMouse: true - background: Item {} - validator: RegExpValidator { regExp: new RegExp("^" + base.currency + " ([0-9]+[.]?[0-9]*)?$") } - } - - property int decimals: 2 - - valueFromText: function(text) { - // remove all non-number tokens from input string so value can be parsed correctly - var value = Number(text.replace(",", ".").replace(/[^0-9.]+/g, "")); - var precision = Math.pow(10, spoolCostSpinBox.decimals); - return Math.round(value * precision) / precision; - } - - textFromValue: function(value) { - return base.currency + " " + value.toFixed(spoolCostSpinBox.decimals) - } + prefix: base.currency + " " + decimals: 2 onValueChanged: { - base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value, decimals)) + base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value)) updateCostPerMeter() } } Label { width: informationPage.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } - SpinBox + Cura.SpinBox { id: spoolWeightSpinBox width: informationPage.columnWidth @@ -328,24 +311,7 @@ Item stepSize: 100 to: 10000 editable: true - - contentItem: TextField - { - text: spoolWeightSpinBox.textFromValue(spoolWeightSpinBox.value, spoolWeightSpinBox.locale) - selectByMouse: true - background: Item {} - validator: RegExpValidator { regExp: new RegExp("^([0-9]+[.]?[0-9]*)? g$") } - } - - valueFromText: function(text, locale) { - // remove all non-number tokens from input string so value can be parsed correctly - var value = Number(text.replace(",", ".").replace(/[^0-9.]+/g, "")); - return Math.round(value); - } - - textFromValue: function(value, locale) { - return value + " g" - } + suffix: " g" onValueChanged: { @@ -465,7 +431,7 @@ Item elide: Text.ElideRight verticalAlignment: Qt.AlignVCenter } - ReadOnlySpinBox + Cura.SpinBox { id: spinBox anchors.left: label.right @@ -489,9 +455,8 @@ Item return 0; } width: base.secondColumnWidth - readOnly: !base.editingEnabled suffix: " " + model.unit - maximumValue: 99999 + to: 99999 decimals: model.unit == "mm" ? 2 : 0 onEditingFinished: materialPropertyProvider.setPropertyValue("value", value) diff --git a/resources/qml/Preferences/ReadOnlySpinBox.qml b/resources/qml/Preferences/ReadOnlySpinBox.qml deleted file mode 100644 index 11e47b38b2..0000000000 --- a/resources/qml/Preferences/ReadOnlySpinBox.qml +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2016 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.1 -import QtQuick.Controls 1.1 -import QtQuick.Dialogs 1.2 - -Item -{ - id: base - - property alias value: spinBox.value - property alias minimumValue: spinBox.minimumValue - property alias maximumValue: spinBox.maximumValue - property alias stepSize: spinBox.stepSize - property alias prefix: spinBox.prefix - property alias suffix: spinBox.suffix - property alias decimals: spinBox.decimals - - signal editingFinished(); - - property bool readOnly: false - - width: spinBox.width - height: spinBox.height - - SpinBox - { - id: spinBox - - enabled: !base.readOnly - opacity: base.readOnly ? 0.5 : 1.0 - - anchors.fill: parent - - onEditingFinished: base.editingFinished() - Keys.onEnterPressed: spinBox.focus = false - Keys.onReturnPressed: spinBox.focus = false - } - - Label - { - visible: base.readOnly - text: base.prefix + base.value.toFixed(spinBox.decimals) + base.suffix - - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: spinBox.__style ? spinBox.__style.padding.left : 0 - - color: palette.buttonText - } - - SystemPalette { id: palette } -} diff --git a/resources/qml/SpinBox.qml b/resources/qml/SpinBox.qml new file mode 100644 index 0000000000..e71435d8c9 --- /dev/null +++ b/resources/qml/SpinBox.qml @@ -0,0 +1,47 @@ +// Copyright (c) 2022 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.15 + +SpinBox +{ + id: base + + property string prefix: "" + property string suffix: "" + property int decimals: 0 + + signal editingFinished() + + valueFromText: function(text) + { + return parseFloat(text.substring(prefix.length, text.length - suffix.length), decimals); + } + + textFromValue: function(value) + { + return prefix + value.toFixed(decimals) + suffix + } + + validator: RegExpValidator + { + regExp: new RegExp("^" + prefix + "([0-9]+[.|,]?[0-9]*)?" + suffix + "$") + } + + contentItem: TextField + { + text: base.textFromValue(base.value, base.locale) + selectByMouse: true + background: Item {} + validator: base.validator + + onActiveFocusChanged: + { + if(!activeFocus) + { + base.editingFinished() + } + } + } +} \ No newline at end of file From 50820048f7046e65eac2c4d5ee6a815d11f6761b Mon Sep 17 00:00:00 2001 From: casper Date: Tue, 8 Feb 2022 11:34:54 +0100 Subject: [PATCH 2/6] Update SpinBox in ContextMenu --- resources/qml/Menus/ContextMenu.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 4ca51c0974..aeb6eb47f9 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Ultimaker B.V. +// Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -120,9 +120,10 @@ Menu anchors.verticalCenter: copiesField.verticalCenter } - SpinBox + Cura.SpinBox { id: copiesField + editable: true focus: true from: 1 to: 99 From a0e5d662998641af1b5155182b949f308c194263 Mon Sep 17 00:00:00 2001 From: casper Date: Tue, 8 Feb 2022 12:01:42 +0100 Subject: [PATCH 3/6] Disable stepSize in all SpinBoxes where stepSize is a non integer value This should be enabled. However this is a feature gap of QtControls 2.x --- resources/qml/Preferences/Materials/MaterialsView.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Preferences/Materials/MaterialsView.qml b/resources/qml/Preferences/Materials/MaterialsView.qml index 283d7a84d2..a3aa451963 100644 --- a/resources/qml/Preferences/Materials/MaterialsView.qml +++ b/resources/qml/Preferences/Materials/MaterialsView.qml @@ -243,7 +243,7 @@ Item value: properties.density decimals: 2 suffix: " g/cm³" - stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size +// stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value) onValueChanged: updateCostPerMeter() @@ -258,7 +258,7 @@ Item value: properties.diameter decimals: 2 suffix: " mm" - stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size +// stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size onEditingFinished: { From 9c2d370e724deba3eed2af0273407eee7cfd4cf9 Mon Sep 17 00:00:00 2001 From: casper Date: Tue, 8 Feb 2022 15:52:49 +0100 Subject: [PATCH 4/6] Allow `SpinBox` components to contain fractional values --- .../Preferences/Materials/MaterialsView.qml | 16 ++--- resources/qml/SpinBox.qml | 70 +++++++++++++------ 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/resources/qml/Preferences/Materials/MaterialsView.qml b/resources/qml/Preferences/Materials/MaterialsView.qml index a3aa451963..6511812057 100644 --- a/resources/qml/Preferences/Materials/MaterialsView.qml +++ b/resources/qml/Preferences/Materials/MaterialsView.qml @@ -243,7 +243,7 @@ Item value: properties.density decimals: 2 suffix: " g/cm³" -// stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size + stepSize: 0.01 onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value) onValueChanged: updateCostPerMeter() @@ -258,7 +258,7 @@ Item value: properties.diameter decimals: 2 suffix: " mm" -// stepSize: 0.01 // spinboxes can only cointain reals, a non-integer value can not be entered as the step size + stepSize: 0.01 onEditingFinished: { @@ -389,12 +389,6 @@ Item Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } } - - function updateCostPerMeter() - { - base.spoolLength = calculateSpoolLength(diameterSpinBox.value, densitySpinBox.value, spoolWeightSpinBox.value); - base.costPerMeter = calculateCostPerMeter(spoolCostSpinBox.value); - } } ListView @@ -606,4 +600,10 @@ Item base.setMetaDataEntry("brand", old_brand, new_brand) properties.brand = new_brand } + + function updateCostPerMeter() + { + base.spoolLength = calculateSpoolLength(diameterSpinBox.value, densitySpinBox.value, spoolWeightSpinBox.value); + base.costPerMeter = calculateCostPerMeter(spoolCostSpinBox.value); + } } diff --git a/resources/qml/SpinBox.qml b/resources/qml/SpinBox.qml index e71435d8c9..956ffdaa10 100644 --- a/resources/qml/SpinBox.qml +++ b/resources/qml/SpinBox.qml @@ -4,44 +4,74 @@ import QtQuick 2.2 import QtQuick.Controls 2.15 -SpinBox +Item { id: base + height: spinBox.height + property string prefix: "" property string suffix: "" property int decimals: 0 + property real stepSize: 1 + property real value: 0 + property real from: 0 + property real to: 99 - signal editingFinished() + property alias wrap: spinBox.wrap - valueFromText: function(text) - { - return parseFloat(text.substring(prefix.length, text.length - suffix.length), decimals); - } + property bool editable: true - textFromValue: function(value) - { - return prefix + value.toFixed(decimals) + suffix - } - - validator: RegExpValidator + property var validator: RegExpValidator { regExp: new RegExp("^" + prefix + "([0-9]+[.|,]?[0-9]*)?" + suffix + "$") } - contentItem: TextField + signal editingFinished() + + SpinBox { - text: base.textFromValue(base.value, base.locale) - selectByMouse: true - background: Item {} + id: spinBox + anchors.fill: base + + stepSize: 1 + + value: Math.floor(base.value / base.stepSize) + from: Math.floor(base.from / base.stepSize) + to: Math.floor(base.to / base.stepSize) + editable: base.editable + + valueFromText: function(text) + { + return parseFloat(text.substring(prefix.length, text.length - suffix.length)) / base.stepSize; + } + + textFromValue: function(value) + { + return prefix + (value * base.stepSize).toFixed(decimals) + suffix; + } + validator: base.validator - onActiveFocusChanged: + onValueModified: { - if(!activeFocus) + base.value = value * base.stepSize; + } + + contentItem: TextField + { + text: spinBox.textFromValue(spinBox.value, spinBox.locale) + selectByMouse: base.editable + background: Item {} + validator: base.validator + + onActiveFocusChanged: { - base.editingFinished() + if(!activeFocus) + { + base.editingFinished(); + } } } } -} \ No newline at end of file +} From 3a74417e6e2cd0f77dbed757c79b33993dd8d3ed Mon Sep 17 00:00:00 2001 From: casper Date: Tue, 8 Feb 2022 16:01:29 +0100 Subject: [PATCH 5/6] Add comments --- resources/qml/SpinBox.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/qml/SpinBox.qml b/resources/qml/SpinBox.qml index 956ffdaa10..4f927d2f25 100644 --- a/resources/qml/SpinBox.qml +++ b/resources/qml/SpinBox.qml @@ -4,6 +4,11 @@ import QtQuick 2.2 import QtQuick.Controls 2.15 +// This component extends the funtionality of QtControls 2.x Spinboxes to +// - be able to contain fractional values +// - hava a "prefix" and a "suffix". A validator is added that recognizes this pre-, suf-fix combo. When adding a custom +// validator the pre-, suf-fix should be added (e.g. new RegExp("^" + prefix + \regex\ + suffix + "$") + Item { id: base @@ -33,13 +38,16 @@ Item { id: spinBox anchors.fill: base + editable: base.editable + // The stepSize of the SpinBox is intentionally set to be always `1` + // As SpinBoxes can only contain integer values the `base.stepSize` is concidered the precision/resolution + // increasing the spinBox.value by one increases the actual/real value of the component by `base.stepSize` + // as such spinBox.value * base.stepSizes produces the real value of the component stepSize: 1 - value: Math.floor(base.value / base.stepSize) from: Math.floor(base.from / base.stepSize) to: Math.floor(base.to / base.stepSize) - editable: base.editable valueFromText: function(text) { From 4737fed9d4914d0641eded413704572cc2342b55 Mon Sep 17 00:00:00 2001 From: casper Date: Tue, 8 Feb 2022 16:48:10 +0100 Subject: [PATCH 6/6] Allow commas as number input --- resources/qml/SpinBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/SpinBox.qml b/resources/qml/SpinBox.qml index 4f927d2f25..7f30109380 100644 --- a/resources/qml/SpinBox.qml +++ b/resources/qml/SpinBox.qml @@ -51,7 +51,7 @@ Item valueFromText: function(text) { - return parseFloat(text.substring(prefix.length, text.length - suffix.length)) / base.stepSize; + return parseFloat(text.substring(prefix.length, text.length - suffix.length).replace(",", ".")) / base.stepSize; } textFromValue: function(value)