diff --git a/src/core/processing/processingalgorithm.cpp b/src/core/processing/processingalgorithm.cpp index 424e38c7e6..4a8ad7fe48 100644 --- a/src/core/processing/processingalgorithm.cpp +++ b/src/core/processing/processingalgorithm.cpp @@ -211,20 +211,13 @@ bool ProcessingAlgorithm::run( bool previewMode ) } else { - if ( outputFeatures.isEmpty() ) - { - // Algorithm deleted the feature, remove from the layer - mInPlaceLayer->deleteFeature( feature.id() ); - } - else if ( outputFeatures.size() == 1 ) - { - // Algorithm modified the feature, adjust accordingly - QgsGeometry outputGeometry = outputFeatures[0].geometry(); + auto updateOriginalFeature = [=]( const QgsFeature &outputFeature ) { + QgsGeometry outputGeometry = outputFeature.geometry(); if ( !outputGeometry.equals( feature.geometry() ) ) { mInPlaceLayer->changeGeometry( feature.id(), outputGeometry ); } - if ( outputFeatures[0].attributes() != feature.attributes() ) + if ( outputFeature.attributes() != feature.attributes() ) { QgsAttributeMap newAttributes; QgsAttributeMap oldAttributes; @@ -232,28 +225,46 @@ bool ProcessingAlgorithm::run( bool previewMode ) for ( const QgsField &field : fields ) { const int index = fields.indexOf( field.name() ); - if ( outputFeatures[0].attribute( index ) != feature.attribute( index ) ) + if ( outputFeature.attribute( index ) != feature.attribute( index ) ) { - newAttributes[index] = outputFeatures[0].attribute( index ); + newAttributes[index] = outputFeature.attribute( index ); oldAttributes[index] = feature.attribute( index ); } } mInPlaceLayer->changeAttributeValues( feature.id(), newAttributes, oldAttributes ); } + }; + + if ( outputFeatures.isEmpty() ) + { + // Algorithm deleted the feature, remove from the layer + mInPlaceLayer->deleteFeature( feature.id() ); + } + else if ( outputFeatures.size() == 1 ) + { + // Algorithm modified the feature, adjust accordingly + updateOriginalFeature( outputFeatures[0] ); } else if ( outputFeatures.size() > 1 ) { QgsFeatureList newFeatures; + bool originalFeatureUpdated = false; for ( QgsFeature &outputFeature : outputFeatures ) { + if ( !originalFeatureUpdated ) + { + updateOriginalFeature( outputFeature ); + originalFeatureUpdated = true; + continue; + } newFeatures << QgsVectorLayerUtils::createFeature( mInPlaceLayer.data(), outputFeature.geometry(), outputFeature.attributes().toMap(), &context.expressionContext() ); } mInPlaceLayer->addFeatures( newFeatures ); } - - mInPlaceLayer->commitChanges(); } } + + mInPlaceLayer->commitChanges(); } else { diff --git a/src/core/processing/processingalgorithmparametersmodel.cpp b/src/core/processing/processingalgorithmparametersmodel.cpp index 29bf823e55..8f8a377cfd 100644 --- a/src/core/processing/processingalgorithmparametersmodel.cpp +++ b/src/core/processing/processingalgorithmparametersmodel.cpp @@ -18,6 +18,7 @@ #include "processingalgorithmparametersmodel.h" #include +#include #include #include #include @@ -29,6 +30,7 @@ ProcessingAlgorithmParametersModel::ProcessingAlgorithmParametersModel( QObject { setSourceModel( mModel ); connect( mModel, &ProcessingAlgorithmParametersModelBase::algorithmIdChanged, this, &ProcessingAlgorithmParametersModel::algorithmIdChanged ); + connect( mModel, &ProcessingAlgorithmParametersModelBase::inPlaceLayerChanged, this, &ProcessingAlgorithmParametersModel::inPlaceLayerChanged ); connect( mModel, &ProcessingAlgorithmParametersModelBase::parametersChanged, this, &ProcessingAlgorithmParametersModel::parametersChanged ); } @@ -55,6 +57,16 @@ void ProcessingAlgorithmParametersModel::setAlgorithmId( const QString &id ) mModel->setAlgorithmId( id ); } +QgsVectorLayer *ProcessingAlgorithmParametersModel::inPlaceLayer() const +{ + return mModel->inPlaceLayer(); +} + +void ProcessingAlgorithmParametersModel::setInPlaceLayer( QgsVectorLayer *layer ) +{ + mModel->setInPlaceLayer( layer ); +} + bool ProcessingAlgorithmParametersModel::isValid() const { return mModel->isValid(); @@ -128,7 +140,7 @@ void ProcessingAlgorithmParametersModelBase::rebuild() if ( mAlgorithm ) { - const static QStringList sSupportedParameters = { QStringLiteral( "number" ) }; + const static QStringList sSupportedParameters = { QStringLiteral( "number" ), QStringLiteral( "distance" ), QStringLiteral( "enum" ) }; const QgsProcessingAlgorithm *algorithm = QgsApplication::instance()->processingRegistry()->algorithmById( mAlgorithmId ); for ( const QgsProcessingParameterDefinition *definition : algorithm->parameterDefinitions() ) { @@ -164,6 +176,21 @@ void ProcessingAlgorithmParametersModelBase::setAlgorithmId( const QString &id ) emit parametersChanged(); } +void ProcessingAlgorithmParametersModelBase::setInPlaceLayer( QgsVectorLayer *layer ) +{ + if ( mInPlaceLayer == layer ) + { + return; + } + + mInPlaceLayer = layer; + + rebuild(); + + emit inPlaceLayerChanged(); + emit parametersChanged(); +} + QString ProcessingAlgorithmParametersModelBase::algorithmDisplayName() const { return mAlgorithm ? mAlgorithm->displayName() : QString(); @@ -237,12 +264,22 @@ QVariant ProcessingAlgorithmParametersModelBase::data( const QModelIndex &index, return mValues.at( index.row() ); case ParameterConfigurationRole: QVariantMap configuration; - if ( const QgsProcessingParameterNumber *parameterNumber = dynamic_cast( mParameters.at( index.row() ) ) ) + if ( const QgsProcessingParameterDistance *parameterDistance = dynamic_cast( mParameters.at( index.row() ) ) ) + { + configuration["minimum"] = parameterDistance->minimum(); + configuration["maximum"] = parameterDistance->maximum(); + configuration["distanceUnit"] = static_cast( ( mInPlaceLayer ? mInPlaceLayer->crs().mapUnits() : Qgis::DistanceUnit::Unknown ) ); + } + else if ( const QgsProcessingParameterNumber *parameterNumber = dynamic_cast( mParameters.at( index.row() ) ) ) { configuration["minimum"] = parameterNumber->minimum(); configuration["maximum"] = parameterNumber->maximum(); configuration["dataType"] = static_cast( parameterNumber->dataType() ); } + else if ( const QgsProcessingParameterEnum *parameterEnum = dynamic_cast( mParameters.at( index.row() ) ) ) + { + configuration["options"] = parameterEnum->options(); + } return configuration; } diff --git a/src/core/processing/processingalgorithmparametersmodel.h b/src/core/processing/processingalgorithmparametersmodel.h index 88810ea792..13b7acccc9 100644 --- a/src/core/processing/processingalgorithmparametersmodel.h +++ b/src/core/processing/processingalgorithmparametersmodel.h @@ -18,6 +18,8 @@ #ifndef PROCESSINGALGORITHMPARAMETERSMODEL #define PROCESSINGALGORITHMPARAMETERSMODEL +#include "qgsvectorlayer.h" + #include #include @@ -39,6 +41,8 @@ class ProcessingAlgorithmParametersModel : public QSortFilterProxyModel Q_PROPERTY( ProcessingAlgorithmParametersModel::Filters filters READ filters WRITE setFilters NOTIFY filtersChanged ) Q_PROPERTY( QString algorithmId READ algorithmId WRITE setAlgorithmId NOTIFY algorithmIdChanged ) + Q_PROPERTY( QgsVectorLayer *inPlaceLayer READ inPlaceLayer WRITE setInPlaceLayer NOTIFY inPlaceLayerChanged ) + Q_PROPERTY( bool isValid READ isValid NOTIFY algorithmIdChanged ) Q_PROPERTY( bool hasParameters READ hasParameters NOTIFY algorithmIdChanged ) Q_PROPERTY( bool hasAdvancedParameters READ hasAdvancedParameters NOTIFY algorithmIdChanged ) @@ -81,6 +85,16 @@ class ProcessingAlgorithmParametersModel : public QSortFilterProxyModel */ void setAlgorithmId( const QString &id ); + /** + * Returns the vector \a layer for in-place algorithms for parameters to take details from. + */ + QgsVectorLayer *inPlaceLayer() const; + + /** + * Sets the vector \a layer for in-place algorithm filter for parameters to take details from. + */ + void setInPlaceLayer( QgsVectorLayer *layer ); + /** * Returns whether the current model refers to a valid algorithm. */ @@ -129,6 +143,11 @@ class ProcessingAlgorithmParametersModel : public QSortFilterProxyModel */ void algorithmIdChanged( const QString &id ); + /** + * Emitted when the in place vector layer has changed + */ + void inPlaceLayerChanged(); + /** * Emitted when the parameters have changed. */ @@ -171,6 +190,16 @@ class ProcessingAlgorithmParametersModelBase : public QAbstractListModel */ void setAlgorithmId( const QString &id ); + /** + * Returns the vector \a layer for in-place algorithms for parameters to take details from. + */ + QgsVectorLayer *inPlaceLayer() const { return mInPlaceLayer.data(); } + + /** + * Sets the vector \a layer for in-place algorithm filter for parameters to take details from. + */ + void setInPlaceLayer( QgsVectorLayer *layer ); + /** * Returns whether the current model refers to a valid algorithm. */ @@ -217,6 +246,11 @@ class ProcessingAlgorithmParametersModelBase : public QAbstractListModel */ void algorithmIdChanged( const QString &id ); + /** + * Emitted when the in place vector layer has changed + */ + void inPlaceLayerChanged(); + /** * Emitted when the parameters have changed. */ @@ -231,6 +265,8 @@ class ProcessingAlgorithmParametersModelBase : public QAbstractListModel QString mAlgorithmId; const QgsProcessingAlgorithm *mAlgorithm = nullptr; + QPointer mInPlaceLayer; + bool mHasAdvancedParameters = false; QList mParameters; diff --git a/src/core/processing/processingalgorithmsmodel.cpp b/src/core/processing/processingalgorithmsmodel.cpp index 90cfff47e4..eddb507383 100644 --- a/src/core/processing/processingalgorithmsmodel.cpp +++ b/src/core/processing/processingalgorithmsmodel.cpp @@ -158,7 +158,7 @@ void ProcessingAlgorithmsModelBase::addProvider( QgsProcessingProvider *provider const QList algorithms = provider->algorithms(); for ( const QgsProcessingAlgorithm *algorithm : algorithms ) { - const static QStringList sSupportedParameters = { QStringLiteral( "number" ), QStringLiteral( "sink" ), QStringLiteral( "source" ) }; + const static QStringList sSupportedParameters = { QStringLiteral( "number" ), QStringLiteral( "distance" ), QStringLiteral( "enum" ), QStringLiteral( "sink" ), QStringLiteral( "source" ) }; const QgsProcessingFeatureBasedAlgorithm *featureBasedAlgorithm = dynamic_cast( algorithm ); if ( featureBasedAlgorithm ) { diff --git a/src/qml/FeatureListForm.qml b/src/qml/FeatureListForm.qml index bb538f1ee1..89a70a7b31 100644 --- a/src/qml/FeatureListForm.qml +++ b/src/qml/FeatureListForm.qml @@ -443,6 +443,7 @@ Rectangle { ProcessingAlgorithmForm { id: processingAlgorithmForm + inPlaceLayer: processingAlgorithm.inPlaceLayer algorithmId: processingAlgorithm.id anchors.top: featureListToolBar.bottom diff --git a/src/qml/LayerLoginDialog.qml b/src/qml/LayerLoginDialog.qml index 1bc556a14d..c5527647cf 100644 --- a/src/qml/LayerLoginDialog.qml +++ b/src/qml/LayerLoginDialog.qml @@ -111,12 +111,9 @@ Page { echoMode: TextInput.Password inputMethodHints: Qt.ImhHiddenText | Qt.ImhNoPredictiveText | Qt.ImhSensitiveData | Qt.ImhNoAutoUppercase | Qt.ImhPreferLowercase horizontalAlignment: Text.AlignHCenter - onReturnPressed: { - _processAuth(); - } - Keys.onEnterPressed: { - _processAuth(); - } + + Keys.onReturnPressed: _processAuth() + Keys.onEnterPressed: _processAuth() } Item { diff --git a/src/qml/ProcessingAlgorithmForm.qml b/src/qml/ProcessingAlgorithmForm.qml index 538b0e610a..22580725d5 100644 --- a/src/qml/ProcessingAlgorithmForm.qml +++ b/src/qml/ProcessingAlgorithmForm.qml @@ -10,6 +10,7 @@ Item { id: processingAlgorithmForm property ProcessingAlgorithmParametersModel algorithmParametersModel: processingAlgorithmParametersModel + property alias inPlaceLayer: processingAlgorithmParametersModel.inPlaceLayer property alias algorithmId: processingAlgorithmParametersModel.algorithmId property alias algorithmDisplayName: processingAlgorithmParametersModel.algorithmDisplayName property alias algorithmShortHelp: processingAlgorithmParametersModel.algorithmShortHelp diff --git a/src/qml/QFieldCloudLogin.qml b/src/qml/QFieldCloudLogin.qml index cc2c131705..1962c2e00b 100644 --- a/src/qml/QFieldCloudLogin.qml +++ b/src/qml/QFieldCloudLogin.qml @@ -99,14 +99,13 @@ Item { Layout.alignment: Qt.AlignHCenter visible: cloudConnection.status === QFieldCloudConnection.Disconnected && (prefixUrlWithProtocol(cloudConnection.url) !== cloudConnection.defaultUrl || isServerUrlEditingActive) enabled: visible - height: Math.max(fontMetrics.height, fontMetrics.boundingRect(text).height) + 34 font: Theme.defaultFont horizontalAlignment: Text.AlignHCenter text: prefixUrlWithProtocol(cloudConnection.url) === cloudConnection.defaultUrl ? '' : cloudConnection.url onTextChanged: text = text.replace(/\s+/g, '') onEditingFinished: cloudConnection.url = text ? prefixUrlWithProtocol(text) : cloudConnection.defaultUrl - onReturnPressed: loginFormSumbitHandler() + Keys.onReturnPressed: loginFormSumbitHandler() function prefixUrlWithProtocol(url) { if (!url || url.startsWith('http://') || url.startsWith('https://')) @@ -132,12 +131,11 @@ Item { Layout.alignment: Qt.AlignHCenter visible: cloudConnection.status === QFieldCloudConnection.Disconnected enabled: visible - height: Math.max(fontMetrics.height, fontMetrics.boundingRect(text).height) + 34 font: Theme.defaultFont horizontalAlignment: Text.AlignHCenter onTextChanged: text = text.replace(/\s+/g, '') - onReturnPressed: loginFormSumbitHandler() + Keys.onReturnPressed: loginFormSumbitHandler() } Text { @@ -158,11 +156,10 @@ Item { Layout.alignment: Qt.AlignHCenter visible: cloudConnection.status === QFieldCloudConnection.Disconnected enabled: visible - height: Math.max(fontMetrics.height, fontMetrics.boundingRect(text).height) + 34 font: Theme.defaultFont horizontalAlignment: Text.AlignHCenter - onReturnPressed: loginFormSumbitHandler() + Keys.onReturnPressed: loginFormSumbitHandler() } FontMetrics { diff --git a/src/qml/editorwidgets/ValueMap.qml b/src/qml/editorwidgets/ValueMap.qml index e5017c9fe4..0d20ed0d56 100644 --- a/src/qml/editorwidgets/ValueMap.qml +++ b/src/qml/editorwidgets/ValueMap.qml @@ -177,7 +177,7 @@ EditorWidgetBase { } } - ComboBox { + QfComboBox { id: comboBox Layout.fillWidth: true font: Theme.defaultFont @@ -219,40 +219,6 @@ EditorWidgetBase { onPositionChanged: mouse.accepted = false onPressAndHold: mouse.accepted = false } - - contentItem: Text { - leftPadding: enabled ? 5 : 0 - - text: comboBox.displayText - font: comboBox.font - color: enabled ? Theme.mainTextColor : Theme.mainTextDisabledColor - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideRight - } - - background: Item { - implicitWidth: 120 - implicitHeight: 36 - - Rectangle { - visible: !enabled - y: comboBox.height - 2 - width: comboBox.width - height: comboBox.activeFocus ? 2 : 1 - color: comboBox.activeFocus ? Theme.accentColor : Theme.accentLightColor - } - - Rectangle { - id: backgroundRect - visible: enabled - anchors.fill: parent - border.color: comboBox.pressed ? Theme.accentColor : Theme.accentLightColor - border.width: comboBox.visualFocus ? 2 : 1 - color: Theme.controlBackgroundAlternateColor - radius: 2 - } - } } FontMetrics { diff --git a/src/qml/imports/Theme/QfComboBox.qml b/src/qml/imports/Theme/QfComboBox.qml new file mode 100644 index 0000000000..5d7444ff2c --- /dev/null +++ b/src/qml/imports/Theme/QfComboBox.qml @@ -0,0 +1,43 @@ +import QtQuick.Controls 2.14 +import QtQuick.Controls.impl 2.14 +import QtQuick 2.14 +import QtQuick.Controls.Material 2.14 +import QtQuick.Controls.Material.impl 2.14 + +ComboBox { + id: comboBox + + contentItem: Text { + leftPadding: enabled ? 5 : 0 + + text: comboBox.displayText + font: comboBox.font + color: enabled ? Theme.mainTextColor : Theme.mainTextDisabledColor + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + } + + background: Item { + implicitWidth: 120 + implicitHeight: 36 + + Rectangle { + visible: !enabled + y: comboBox.height - 2 + width: comboBox.width + height: comboBox.activeFocus ? 2 : 1 + color: comboBox.activeFocus ? Theme.accentColor : Theme.accentLightColor + } + + Rectangle { + id: backgroundRect + visible: enabled + anchors.fill: parent + border.color: comboBox.pressed ? Theme.accentColor : Theme.accentLightColor + border.width: comboBox.visualFocus ? 2 : 1 + color: Theme.controlBackgroundAlternateColor + radius: 2 + } + } +} diff --git a/src/qml/imports/Theme/QfTextField.qml b/src/qml/imports/Theme/QfTextField.qml index 96f742e78d..37fbbb92be 100644 --- a/src/qml/imports/Theme/QfTextField.qml +++ b/src/qml/imports/Theme/QfTextField.qml @@ -1,90 +1,51 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 -Item { - id: textFieldWrapper - - property alias font: textField.font - property alias text: textField.text - property alias displayText: textField.displayText - property alias placeholderText: textField.placeholderText - property alias horizontalAlignment: textField.horizontalAlignment - property alias inputMethodHints: textField.inputMethodHints - property alias inputMask: textField.inputMask - property alias validator: textField.validator - - property int echoMode: TextInput.Normal - - signal textEdited - signal editingFinished - signal returnPressed - - width: textField.width - height: textField.height - - FontMetrics { - id: fontMetrics - font: Theme.defaultFont - } - - TextField { - id: textField - echoMode: textFieldWrapper.echoMode - width: textFieldWrapper.width - font: Theme.defaultFont - placeholderTextColor: Theme.accentLightColor - rightPadding: showPasswordButton.visible ? showPasswordButton.width : 0 - leftPadding: rightPadding - inputMethodHints: Qt.ImhNone - - background: Rectangle { - implicitWidth: 120 - color: "transparent" - - Rectangle { - y: textField.height - height - textField.bottomPadding / 2 - width: textField.width - height: textField.activeFocus ? 2 : 1 - color: textField.activeFocus ? Theme.accentColor : Theme.accentLightColor - } - } - - onActiveFocusChanged: { - if (!activeFocus) { - echoMode = textFieldWrapper.echoMode; - } - } - - onTextEdited: { - textFieldWrapper.textEdited(); - } - - onEditingFinished: { - textFieldWrapper.editingFinished(); - } - - onFocusChanged: { - if (focus) { - Qt.inputMethod.show(); - } +TextField { + id: textField + echoMode: TextInput.Normal + font: Theme.defaultFont + placeholderTextColor: Theme.accentLightColor + rightPadding: showPasswordButton.visible ? showPasswordButton.width : 0 + leftPadding: rightPadding + topPadding: 10 + bottomPadding: 10 + inputMethodHints: Qt.ImhNone + + background: Rectangle { + implicitWidth: 120 + color: "transparent" + + Rectangle { + y: textField.height - height - textField.bottomPadding / 2 + width: textField.width + height: textField.activeFocus ? 2 : 1 + color: textField.activeFocus ? Theme.accentColor : Theme.accentLightColor } + } - Keys.onReturnPressed: { - textFieldWrapper.returnPressed(); + onFocusChanged: { + if (focus) { + Qt.inputMethod.show(); } } QfToolButton { id: showPasswordButton z: 1 - visible: !!textFieldWrapper.echoMode && textFieldWrapper.echoMode !== TextInput.Normal + visible: !!textField.echoMode && textField.echoMode !== TextInput.Normal iconSource: textField.echoMode === TextInput.Normal ? Theme.getThemeVectorIcon('ic_hide_green_48dp') : Theme.getThemeVectorIcon('ic_show_green_48dp') anchors.right: textField.right anchors.verticalCenter: textField.verticalCenter opacity: textField.text.length > 0 ? 1 : 0.25 onClicked: { - textField.echoMode = textField.echoMode === TextInput.Normal ? textFieldWrapper.echoMode : TextInput.Normal; + textField.echoMode = textField.echoMode === TextInput.Normal ? textField.echoMode : TextInput.Normal; } } + + FontMetrics { + id: fontMetrics + font: Theme.defaultFont + } } diff --git a/src/qml/imports/Theme/qmldir b/src/qml/imports/Theme/qmldir index f61dd1f4e7..5a9f6d4046 100644 --- a/src/qml/imports/Theme/qmldir +++ b/src/qml/imports/Theme/qmldir @@ -1,5 +1,6 @@ module Theme singleton Theme 1.0 Theme.qml +QfComboBox 1.0 QfComboBox.qml QfCloseButton 1.0 QfCloseButton.qml QfSlider 1.0 QfSlider.qml QfSwitch 1.0 QfSwitch.qml diff --git a/src/qml/processingparameterwidgets/distance.qml b/src/qml/processingparameterwidgets/distance.qml new file mode 100644 index 0000000000..57779c8925 --- /dev/null +++ b/src/qml/processingparameterwidgets/distance.qml @@ -0,0 +1,246 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import Theme 1.0 +import org.qfield 1.0 +import org.qgis 1.0 +import "." + +ProcessingParameterWidgetBase { + id: distanceItem + + property real step: 1 + property real min: configuration['minimum'] + property real max: configuration['maximum'] + property int distanceUnit: configuration['distanceUnit'] + property bool distanceConvertible: distanceUnit !== Qgis.DistanceUnit.Degrees && distanceUnit !== Qgis.DistanceUnit.Unknown + + height: childrenRect.height + + property double currentValue: distanceConvertible ? value * UnitTypes.fromUnitToUnitFactor(distanceUnit, unitTypesComboBox.currentValue) : value + + Row { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: 5 + + QfTextField { + id: textField + height: fontMetrics.height + 20 + width: parent.width - decreaseButton.width - increaseButton.width - (distanceConvertible ? unitTypesComboBox.width : unitTypeLabel.width) - parent.spacing * 3 + + font: Theme.defaultFont + color: value === undefined || !enabled ? Theme.mainTextDisabledColor : Theme.mainTextColor + + text: currentValue !== undefined ? currentValue : '' + + validator: doubleValidator + + inputMethodHints: Qt.ImhFormattedNumbersOnly + + onTextChanged: { + if (parseFloat(text) !== currentValue) { + if (!isNaN(parseFloat(text))) { + let numberValue = Math.max(distanceItem.min, Math.min(distanceItem.max, text)); + prepareValueChangeRequest(numberValue); + } + } + } + } + + Label { + id: unitTypeLabel + visible: !distanceConvertible + width: distanceConvertible ? contentWidth : 0 + font: Theme.defaultFont + color: Theme.mainTextColor + + text: distanceUnit === Qgis.DistanceUnit.Degrees ? qsTr('degrees') : qsTr('') + } + + QfComboBox { + id: unitTypesComboBox + + visible: distanceConvertible + implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted + + textRole: "display" + valueRole: "value" + model: ListModel { + ListElement { + display: qsTr("meters") + value: Qgis.DistanceUnit.Meters + } + ListElement { + display: qsTr("kilometers") + value: Qgis.DistanceUnit.Kilometers + } + ListElement { + display: qsTr("feet") + value: Qgis.DistanceUnit.Feet + } + ListElement { + display: qsTr("yards") + value: Qgis.DistanceUnit.Yards + } + ListElement { + display: qsTr("miles") + value: Qgis.DistanceUnit.Miles + } + ListElement { + display: qsTr("nautical miles") + value: Qgis.DistanceUnit.NauticalMiles + } + ListElement { + display: qsTr("centimeters") + value: Qgis.DistanceUnit.Centimeters + } + ListElement { + display: qsTr("millimeters") + value: Qgis.DistanceUnit.Millimeters + } + ListElement { + display: qsTr("inches") + value: Qgis.DistanceUnit.Inches + } + } + + onCurrentIndexChanged: { + if (!isNaN(parseFloat(textField.text))) { + let numberValue = Math.max(distanceItem.min, Math.min(distanceItem.max, textField.text)); + prepareValueChangeRequest(numberValue); + } + } + + Component.onCompleted: { + if (distanceConvertible) { + for (let i = 0; i < model.count; i++) { + if (model.get(i).value === distanceUnit) { + currentIndex = distanceUnit; + break; + } + } + } + } + } + + QfToolButton { + id: decreaseButton + width: enabled ? 48 : 0 + height: 48 + + anchors.verticalCenter: textField.verticalCenter + + iconSource: Theme.getThemeVectorIcon("ic_remove_black_24dp") + iconColor: Theme.mainTextColor + bgcolor: "transparent" + visible: enabled + + onClicked: { + decreaseValue(); + } + onDoubleClicked: { + decreaseValue(); + } + onPressAndHold: { + changeValueTimer.increase = false; + changeValueTimer.interval = 700; + changeValueTimer.restart(); + } + onReleased: { + changeValueTimer.stop(); + } + onCanceled: { + changeValueTimer.stop(); + } + } + + QfToolButton { + id: increaseButton + width: enabled ? 48 : 0 + height: 48 + + anchors.verticalCenter: textField.verticalCenter + + iconSource: Theme.getThemeVectorIcon("ic_add_black_24dp") + iconColor: Theme.mainTextColor + bgcolor: "transparent" + visible: enabled + + onClicked: { + increaseValue(); + } + onDoubleClicked: { + increaseValue(); + } + onPressAndHold: { + changeValueTimer.increase = true; + changeValueTimer.interval = 700; + changeValueTimer.restart(); + } + onReleased: { + changeValueTimer.stop(); + } + onCanceled: { + changeValueTimer.stop(); + } + } + } + + Timer { + id: changeValueTimer + interval: 700 + repeat: true + + property bool increase: true + + onTriggered: { + var hitBoundary = false; + if (increase) { + adjustValue(1); + hitBoundary = parseFloat(textField.text) === distanceItem.max; + } else { + adjustValue(-1); + hitBoundary = parseFloat(textField.text) === distanceItem.min; + } + if (!hitBoundary) { + if (interval > 50) + interval = interval * 0.7; + } else { + stop(); + } + } + } + + function adjustValue(direction) { + var currentValue = parseFloat(textField.text); + var newValue; + if (!isNaN(currentValue)) { + newValue = currentValue + (distanceItem.step * direction); + prepareValueChangeRequest(Math.min(distanceItem.max(Math.max(distanceItem.min, newValue)))); + } else { + newValue = 0; + prepareValueChangeRequest(newValue, false); + } + } + + FontMetrics { + id: fontMetrics + font: textField.font + } + + DoubleValidator { + id: doubleValidator + locale: 'C' + bottom: distanceItem.min + top: distanceItem.max + } + + function prepareValueChangeRequest(newValue) { + if (distanceConvertible) { + valueChangeRequested(newValue * UnitTypes.fromUnitToUnitFactor(unitTypesComboBox.currentValue, distanceUnit)); + } else { + valueChangeRequested(newValue); + } + } +} diff --git a/src/qml/processingparameterwidgets/enum.qml b/src/qml/processingparameterwidgets/enum.qml new file mode 100644 index 0000000000..38a7f18720 --- /dev/null +++ b/src/qml/processingparameterwidgets/enum.qml @@ -0,0 +1,36 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import Theme 1.0 +import org.qfield 1.0 +import org.qgis 1.0 +import "." + +ProcessingParameterWidgetBase { + id: distanceItem + + height: childrenRect.height + + Row { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: 5 + + QfComboBox { + id: unitTypesComboBox + + width: parent.width + model: configuration["options"] + + onCurrentIndexChanged: { + if (currentIndex != value) { + valueChangeRequested(currentIndex); + } + } + + Component.onCompleted: { + currentIndex = value; + } + } + } +} diff --git a/src/qml/processingparameterwidgets/number.qml b/src/qml/processingparameterwidgets/number.qml index 4187d2a38d..02edbe84b0 100644 --- a/src/qml/processingparameterwidgets/number.qml +++ b/src/qml/processingparameterwidgets/number.qml @@ -19,15 +19,12 @@ ProcessingParameterWidgetBase { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top + spacing: 5 - TextField { + QfTextField { id: textField height: fontMetrics.height + 20 - topPadding: 10 - bottomPadding: 10 - rightPadding: 0 - leftPadding: enabled ? 5 : 0 - width: parent.width - decreaseButton.width - increaseButton.width + width: parent.width - decreaseButton.width - increaseButton.width - parent.spacing * 2 font: Theme.defaultFont color: value === undefined || !enabled ? Theme.mainTextDisabledColor : Theme.mainTextColor @@ -44,18 +41,6 @@ ProcessingParameterWidgetBase { inputMethodHints: Qt.ImhFormattedNumbersOnly - background: Rectangle { - implicitWidth: 120 - color: "transparent" - - Rectangle { - y: textField.height - height - textField.bottomPadding / 2 - width: textField.width - height: textField.activeFocus ? 2 : 1 - color: textField.activeFocus ? Theme.accentColor : Theme.accentLightColor - } - } - onTextChanged: { if (text != value) { if (!isNaN(parseFloat(text))) { diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc index b3db424cea..9705574f5e 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -33,6 +33,8 @@ ProcessingAlgorithmForm.qml ProcessingAlgorithmPreview.qml processingparameterwidgets/ProcessingParameterWidgetBase.qml + processingparameterwidgets/enum.qml + processingparameterwidgets/distance.qml processingparameterwidgets/number.qml QFieldAudioRecorder.qml QFieldCamera.qml @@ -81,6 +83,7 @@ LayerLoginDialog.qml imports/Theme/Theme.qml imports/Theme/QfCloseButton.qml + imports/Theme/QfComboBox.qml imports/Theme/QfDropShadow.qml imports/Theme/QfOpacityMask.qml imports/Theme/QfToolButton.qml