From c0ebc61952e51c03f91eaedada499f7e7fab02aa Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sat, 6 Jul 2024 17:20:47 +0700 Subject: [PATCH 1/9] Add support for distance parameters in processing algorithms --- .../processingalgorithmparametersmodel.cpp | 35 ++- .../processingalgorithmparametersmodel.h | 36 +++ .../processing/processingalgorithmsmodel.cpp | 2 +- src/qml/FeatureListForm.qml | 1 + src/qml/ProcessingAlgorithmForm.qml | 1 + .../processingparameterwidgets/distance.qml | 278 ++++++++++++++++++ src/qml/qml.qrc | 1 + 7 files changed, 352 insertions(+), 2 deletions(-) create mode 100644 src/qml/processingparameterwidgets/distance.qml diff --git a/src/core/processing/processingalgorithmparametersmodel.cpp b/src/core/processing/processingalgorithmparametersmodel.cpp index 29bf823e55..df661420b2 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" ) }; 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(); @@ -243,6 +270,12 @@ QVariant ProcessingAlgorithmParametersModelBase::data( const QModelIndex &index, configuration["maximum"] = parameterNumber->maximum(); configuration["dataType"] = static_cast( parameterNumber->dataType() ); } + 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 ) ); + } 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..2729da3901 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( "sink" ), QStringLiteral( "source" ) }; const QgsProcessingFeatureBasedAlgorithm *featureBasedAlgorithm = dynamic_cast( algorithm ); if ( featureBasedAlgorithm ) { diff --git a/src/qml/FeatureListForm.qml b/src/qml/FeatureListForm.qml index 57bca5bd55..34a1b081e9 100644 --- a/src/qml/FeatureListForm.qml +++ b/src/qml/FeatureListForm.qml @@ -449,6 +449,7 @@ Rectangle { ProcessingAlgorithmForm { id: processingAlgorithmForm + inPlaceLayer: processingAlgorithm.inPlaceLayer algorithmId: processingAlgorithm.id anchors.top: featureListToolBar.bottom 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/processingparameterwidgets/distance.qml b/src/qml/processingparameterwidgets/distance.qml new file mode 100644 index 0000000000..e8afa5ce8a --- /dev/null +++ b/src/qml/processingparameterwidgets/distance.qml @@ -0,0 +1,278 @@ +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 + + TextField { + id: textField + height: fontMetrics.height + 20 + topPadding: 10 + bottomPadding: 10 + rightPadding: 0 + leftPadding: enabled ? 5 : 0 + width: parent.width - decreaseButton.width - increaseButton.width - 10 - (distanceConvertible ? unitTypesComboBox.width : unitTypeLabel.width) + + font: Theme.defaultFont + color: value === undefined || !enabled ? Theme.mainTextDisabledColor : Theme.mainTextColor + + text: currentValue !== undefined ? currentValue : '' + + validator: doubleValidator + + 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 != 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 ? 100 : 0 + font: Theme.defaultFont + color: Theme.mainTextColor + + text: distanceUnit === Qgis.DistanceUnit.Degrees ? qsTr('degrees') : qsTr('') + } + + ComboBox { + id: unitTypesComboBox + + property bool initialized: false + + visible: distanceConvertible + implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted + + textRole: "display" + valueRole: "value" + model: ListModel { + ListElement { + display: "meters" + value: Qgis.DistanceUnit.Meters + } + ListElement { + display: "kilometers" + value: Qgis.DistanceUnit.Kilometers + } + ListElement { + display: "feet" + value: Qgis.DistanceUnit.Feet + } + ListElement { + display: "yards" + value: Qgis.DistanceUnit.Yards + } + ListElement { + display: "miles" + value: Qgis.DistanceUnit.Miles + } + ListElement { + display: "nautical miles" + value: Qgis.DistanceUnit.NauticalMiles + } + ListElement { + display: "centimeters" + value: Qgis.DistanceUnit.Centimeters + } + ListElement { + display: "millimeters" + value: Qgis.DistanceUnit.Millimeters + } + ListElement { + display: "inches" + value: Qgis.DistanceUnit.Inches + } + } + + onCurrentIndexChanged: { + if (textField.text != value) { + 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; + } + } + initialized = true; + } + } + } + + 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) { + increaseValue(); + hitBoundary = textField.text == distanceItem.max; + } else { + decreaseValue(); + hitBoundary = textField.text == distanceItem.min; + } + if (!hitBoundary) { + if (interval > 50) + interval = interval * 0.7; + } else { + stop(); + } + } + } + + function decreaseValue() { + var currentValue = Number.parseFloat(textField.text); + var newValue; + if (!isNaN(currentValue)) { + newValue = currentValue - distanceItem.step; + prepareValueChangeRequest(Math.max(distanceItem.min, newValue)); + } else { + newValue = 0; + prepareValueChangeRequest(newValue, false); + } + } + + function increaseValue() { + var currentValue = Number.parseFloat(textField.text); + var newValue; + if (!isNaN(currentValue)) { + newValue = currentValue + distanceItem.step; + prepareValueChangeRequest(Math.min(distanceItem.max, newValue)); + } else { + newValue = 0; + prepareValueChangeRequest(newValue); + } + } + + 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/qml.qrc b/src/qml/qml.qrc index b3db424cea..576a74c60b 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -33,6 +33,7 @@ ProcessingAlgorithmForm.qml ProcessingAlgorithmPreview.qml processingparameterwidgets/ProcessingParameterWidgetBase.qml + processingparameterwidgets/distance.qml processingparameterwidgets/number.qml QFieldAudioRecorder.qml QFieldCamera.qml From fb21f6881ec8a40e27cb5b63b03e19959246359b Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sat, 6 Jul 2024 18:09:33 +0700 Subject: [PATCH 2/9] Always replace the first feature output by in-place feature-based algorithms --- src/core/processing/processingalgorithm.cpp | 35 ++++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/core/processing/processingalgorithm.cpp b/src/core/processing/processingalgorithm.cpp index 424e38c7e6..4e5653776b 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,20 +225,38 @@ 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 ); From 07e05654d18b8378c5c91cb54a0fefb85674a173 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sat, 6 Jul 2024 18:26:10 +0700 Subject: [PATCH 3/9] Only commit changes once all features are processed --- src/core/processing/processingalgorithm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/processing/processingalgorithm.cpp b/src/core/processing/processingalgorithm.cpp index 4e5653776b..4a8ad7fe48 100644 --- a/src/core/processing/processingalgorithm.cpp +++ b/src/core/processing/processingalgorithm.cpp @@ -261,10 +261,10 @@ bool ProcessingAlgorithm::run( bool previewMode ) } mInPlaceLayer->addFeatures( newFeatures ); } - - mInPlaceLayer->commitChanges(); } } + + mInPlaceLayer->commitChanges(); } else { From 2adb34e8295b0fdc68a147b99f3ebf1e0931382e Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sat, 6 Jul 2024 18:30:14 +0700 Subject: [PATCH 4/9] Make unit types combobox translation-friendly --- .../processingparameterwidgets/distance.qml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/qml/processingparameterwidgets/distance.qml b/src/qml/processingparameterwidgets/distance.qml index e8afa5ce8a..7ce4bb2217 100644 --- a/src/qml/processingparameterwidgets/distance.qml +++ b/src/qml/processingparameterwidgets/distance.qml @@ -85,39 +85,39 @@ ProcessingParameterWidgetBase { valueRole: "value" model: ListModel { ListElement { - display: "meters" + display: qsTr("meters") value: Qgis.DistanceUnit.Meters } ListElement { - display: "kilometers" + display: qsTr("kilometers") value: Qgis.DistanceUnit.Kilometers } ListElement { - display: "feet" + display: qsTr("feet") value: Qgis.DistanceUnit.Feet } ListElement { - display: "yards" + display: qsTr("yards") value: Qgis.DistanceUnit.Yards } ListElement { - display: "miles" + display: qsTr("miles") value: Qgis.DistanceUnit.Miles } ListElement { - display: "nautical miles" + display: qsTr("nautical miles") value: Qgis.DistanceUnit.NauticalMiles } ListElement { - display: "centimeters" + display: qsTr("centimeters") value: Qgis.DistanceUnit.Centimeters } ListElement { - display: "millimeters" + display: qsTr("millimeters") value: Qgis.DistanceUnit.Millimeters } ListElement { - display: "inches" + display: qsTr("inches") value: Qgis.DistanceUnit.Inches } } From 9ecca7c0e3385fcd81c161e4785ab38e9917f36e Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sun, 7 Jul 2024 10:37:49 +0700 Subject: [PATCH 5/9] Improve looks of combo box to match that of our feature form, improve spacing --- .../processing/processingalgorithmsmodel.cpp | 4 ++ src/qml/editorwidgets/ValueMap.qml | 36 +--------------- src/qml/imports/Theme/QfComboBox.qml | 43 +++++++++++++++++++ src/qml/imports/Theme/qmldir | 1 + .../processingparameterwidgets/distance.qml | 5 ++- src/qml/processingparameterwidgets/number.qml | 3 +- src/qml/qml.qrc | 1 + 7 files changed, 55 insertions(+), 38 deletions(-) create mode 100644 src/qml/imports/Theme/QfComboBox.qml diff --git a/src/core/processing/processingalgorithmsmodel.cpp b/src/core/processing/processingalgorithmsmodel.cpp index 2729da3901..8723c8fd92 100644 --- a/src/core/processing/processingalgorithmsmodel.cpp +++ b/src/core/processing/processingalgorithmsmodel.cpp @@ -182,6 +182,10 @@ void ProcessingAlgorithmsModelBase::addProvider( QgsProcessingProvider *provider continue; } + if ( algorithm->flags() & Qgis::ProcessingAlgorithmFlag::SupportsInPlaceEdits ) + { + qDebug() << algorithm->displayName(); + } mAlgorithms << AlgorithmItem( algorithm ); } } 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/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 index 7ce4bb2217..4d478c4c32 100644 --- a/src/qml/processingparameterwidgets/distance.qml +++ b/src/qml/processingparameterwidgets/distance.qml @@ -22,6 +22,7 @@ ProcessingParameterWidgetBase { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top + spacing: 5 TextField { id: textField @@ -30,7 +31,7 @@ ProcessingParameterWidgetBase { bottomPadding: 10 rightPadding: 0 leftPadding: enabled ? 5 : 0 - width: parent.width - decreaseButton.width - increaseButton.width - 10 - (distanceConvertible ? unitTypesComboBox.width : unitTypeLabel.width) + 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 @@ -73,7 +74,7 @@ ProcessingParameterWidgetBase { text: distanceUnit === Qgis.DistanceUnit.Degrees ? qsTr('degrees') : qsTr('') } - ComboBox { + QfComboBox { id: unitTypesComboBox property bool initialized: false diff --git a/src/qml/processingparameterwidgets/number.qml b/src/qml/processingparameterwidgets/number.qml index 4187d2a38d..069cb9b6b6 100644 --- a/src/qml/processingparameterwidgets/number.qml +++ b/src/qml/processingparameterwidgets/number.qml @@ -19,6 +19,7 @@ ProcessingParameterWidgetBase { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top + spacing: 5 TextField { id: textField @@ -27,7 +28,7 @@ ProcessingParameterWidgetBase { 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 diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc index 576a74c60b..23a0b03b13 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -82,6 +82,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 From e6c03f7cc221b2cc8d35aa376daf66bb255ca76e Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sun, 7 Jul 2024 10:50:40 +0700 Subject: [PATCH 6/9] Add support for enum parameters in algorithms --- .../processingalgorithmparametersmodel.cpp | 16 +++++--- .../processing/processingalgorithmsmodel.cpp | 2 +- src/qml/processingparameterwidgets/enum.qml | 38 +++++++++++++++++++ src/qml/qml.qrc | 1 + 4 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 src/qml/processingparameterwidgets/enum.qml diff --git a/src/core/processing/processingalgorithmparametersmodel.cpp b/src/core/processing/processingalgorithmparametersmodel.cpp index df661420b2..8f8a377cfd 100644 --- a/src/core/processing/processingalgorithmparametersmodel.cpp +++ b/src/core/processing/processingalgorithmparametersmodel.cpp @@ -140,7 +140,7 @@ void ProcessingAlgorithmParametersModelBase::rebuild() if ( mAlgorithm ) { - const static QStringList sSupportedParameters = { QStringLiteral( "number" ), QStringLiteral( "distance" ) }; + const static QStringList sSupportedParameters = { QStringLiteral( "number" ), QStringLiteral( "distance" ), QStringLiteral( "enum" ) }; const QgsProcessingAlgorithm *algorithm = QgsApplication::instance()->processingRegistry()->algorithmById( mAlgorithmId ); for ( const QgsProcessingParameterDefinition *definition : algorithm->parameterDefinitions() ) { @@ -264,17 +264,21 @@ 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() ); } - if ( const QgsProcessingParameterDistance *parameterDistance = dynamic_cast( mParameters.at( index.row() ) ) ) + else if ( const QgsProcessingParameterEnum *parameterEnum = 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 ) ); + configuration["options"] = parameterEnum->options(); } return configuration; } diff --git a/src/core/processing/processingalgorithmsmodel.cpp b/src/core/processing/processingalgorithmsmodel.cpp index 8723c8fd92..7bf23dbad0 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( "distance" ), 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/processingparameterwidgets/enum.qml b/src/qml/processingparameterwidgets/enum.qml new file mode 100644 index 0000000000..5007c0c590 --- /dev/null +++ b/src/qml/processingparameterwidgets/enum.qml @@ -0,0 +1,38 @@ +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 + + property bool initialized: false + + width: parent.width + model: configuration["options"] + + onCurrentIndexChanged: { + if (currentIndex != value) { + valueChangeRequested(currentIndex); + } + } + + Component.onCompleted: { + currentIndex = value; + } + } + } +} diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc index 23a0b03b13..9705574f5e 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -33,6 +33,7 @@ ProcessingAlgorithmForm.qml ProcessingAlgorithmPreview.qml processingparameterwidgets/ProcessingParameterWidgetBase.qml + processingparameterwidgets/enum.qml processingparameterwidgets/distance.qml processingparameterwidgets/number.qml QFieldAudioRecorder.qml From 11065d5382a3793bd251b7056c1a5976f7857a9f Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sun, 7 Jul 2024 10:52:14 +0700 Subject: [PATCH 7/9] Remove debug lines --- src/core/processing/processingalgorithmsmodel.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core/processing/processingalgorithmsmodel.cpp b/src/core/processing/processingalgorithmsmodel.cpp index 7bf23dbad0..eddb507383 100644 --- a/src/core/processing/processingalgorithmsmodel.cpp +++ b/src/core/processing/processingalgorithmsmodel.cpp @@ -182,10 +182,6 @@ void ProcessingAlgorithmsModelBase::addProvider( QgsProcessingProvider *provider continue; } - if ( algorithm->flags() & Qgis::ProcessingAlgorithmFlag::SupportsInPlaceEdits ) - { - qDebug() << algorithm->displayName(); - } mAlgorithms << AlgorithmItem( algorithm ); } } From 21791ee2a8cb272e531be5020bf4b59bde6cce8f Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Wed, 10 Jul 2024 08:30:21 +0700 Subject: [PATCH 8/9] Address review --- src/qml/imports/Theme/QfTextField.qml | 3 + .../processingparameterwidgets/distance.qml | 61 +++++-------------- src/qml/processingparameterwidgets/enum.qml | 2 - src/qml/processingparameterwidgets/number.qml | 18 +----- 4 files changed, 18 insertions(+), 66 deletions(-) diff --git a/src/qml/imports/Theme/QfTextField.qml b/src/qml/imports/Theme/QfTextField.qml index 96f742e78d..97d877b87e 100644 --- a/src/qml/imports/Theme/QfTextField.qml +++ b/src/qml/imports/Theme/QfTextField.qml @@ -5,6 +5,7 @@ Item { id: textFieldWrapper property alias font: textField.font + property alias color: textField.color property alias text: textField.text property alias displayText: textField.displayText property alias placeholderText: textField.placeholderText @@ -35,6 +36,8 @@ Item { placeholderTextColor: Theme.accentLightColor rightPadding: showPasswordButton.visible ? showPasswordButton.width : 0 leftPadding: rightPadding + topPadding: 10 + bottomPadding: 10 inputMethodHints: Qt.ImhNone background: Rectangle { diff --git a/src/qml/processingparameterwidgets/distance.qml b/src/qml/processingparameterwidgets/distance.qml index 4d478c4c32..57779c8925 100644 --- a/src/qml/processingparameterwidgets/distance.qml +++ b/src/qml/processingparameterwidgets/distance.qml @@ -24,13 +24,9 @@ ProcessingParameterWidgetBase { 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 - (distanceConvertible ? unitTypesComboBox.width : unitTypeLabel.width) - parent.spacing * 3 font: Theme.defaultFont @@ -42,20 +38,8 @@ 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 != currentValue) { + if (parseFloat(text) !== currentValue) { if (!isNaN(parseFloat(text))) { let numberValue = Math.max(distanceItem.min, Math.min(distanceItem.max, text)); prepareValueChangeRequest(numberValue); @@ -67,7 +51,7 @@ ProcessingParameterWidgetBase { Label { id: unitTypeLabel visible: !distanceConvertible - width: distanceConvertible ? 100 : 0 + width: distanceConvertible ? contentWidth : 0 font: Theme.defaultFont color: Theme.mainTextColor @@ -77,8 +61,6 @@ ProcessingParameterWidgetBase { QfComboBox { id: unitTypesComboBox - property bool initialized: false - visible: distanceConvertible implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted @@ -124,11 +106,9 @@ ProcessingParameterWidgetBase { } onCurrentIndexChanged: { - if (textField.text != value) { - if (!isNaN(parseFloat(textField.text))) { - let numberValue = Math.max(distanceItem.min, Math.min(distanceItem.max, textField.text)); - prepareValueChangeRequest(numberValue); - } + if (!isNaN(parseFloat(textField.text))) { + let numberValue = Math.max(distanceItem.min, Math.min(distanceItem.max, textField.text)); + prepareValueChangeRequest(numberValue); } } @@ -140,7 +120,6 @@ ProcessingParameterWidgetBase { break; } } - initialized = true; } } } @@ -218,11 +197,11 @@ ProcessingParameterWidgetBase { onTriggered: { var hitBoundary = false; if (increase) { - increaseValue(); - hitBoundary = textField.text == distanceItem.max; + adjustValue(1); + hitBoundary = parseFloat(textField.text) === distanceItem.max; } else { - decreaseValue(); - hitBoundary = textField.text == distanceItem.min; + adjustValue(-1); + hitBoundary = parseFloat(textField.text) === distanceItem.min; } if (!hitBoundary) { if (interval > 50) @@ -233,30 +212,18 @@ ProcessingParameterWidgetBase { } } - function decreaseValue() { - var currentValue = Number.parseFloat(textField.text); + function adjustValue(direction) { + var currentValue = parseFloat(textField.text); var newValue; if (!isNaN(currentValue)) { - newValue = currentValue - distanceItem.step; - prepareValueChangeRequest(Math.max(distanceItem.min, newValue)); + newValue = currentValue + (distanceItem.step * direction); + prepareValueChangeRequest(Math.min(distanceItem.max(Math.max(distanceItem.min, newValue)))); } else { newValue = 0; prepareValueChangeRequest(newValue, false); } } - function increaseValue() { - var currentValue = Number.parseFloat(textField.text); - var newValue; - if (!isNaN(currentValue)) { - newValue = currentValue + distanceItem.step; - prepareValueChangeRequest(Math.min(distanceItem.max, newValue)); - } else { - newValue = 0; - prepareValueChangeRequest(newValue); - } - } - FontMetrics { id: fontMetrics font: textField.font diff --git a/src/qml/processingparameterwidgets/enum.qml b/src/qml/processingparameterwidgets/enum.qml index 5007c0c590..38a7f18720 100644 --- a/src/qml/processingparameterwidgets/enum.qml +++ b/src/qml/processingparameterwidgets/enum.qml @@ -19,8 +19,6 @@ ProcessingParameterWidgetBase { QfComboBox { id: unitTypesComboBox - property bool initialized: false - width: parent.width model: configuration["options"] diff --git a/src/qml/processingparameterwidgets/number.qml b/src/qml/processingparameterwidgets/number.qml index 069cb9b6b6..02edbe84b0 100644 --- a/src/qml/processingparameterwidgets/number.qml +++ b/src/qml/processingparameterwidgets/number.qml @@ -21,13 +21,9 @@ ProcessingParameterWidgetBase { 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 - parent.spacing * 2 font: Theme.defaultFont @@ -45,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))) { From 2ecbb938adb93e6717b1c2363d759929b736fe71 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Wed, 10 Jul 2024 08:45:25 +0700 Subject: [PATCH 9/9] Avoid a dozen or so alias for QfTextEdit --- src/qml/LayerLoginDialog.qml | 9 +-- src/qml/QFieldCloudLogin.qml | 9 +-- src/qml/imports/Theme/QfTextField.qml | 104 ++++++++------------------ 3 files changed, 37 insertions(+), 85 deletions(-) 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/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/imports/Theme/QfTextField.qml b/src/qml/imports/Theme/QfTextField.qml index 97d877b87e..37fbbb92be 100644 --- a/src/qml/imports/Theme/QfTextField.qml +++ b/src/qml/imports/Theme/QfTextField.qml @@ -1,93 +1,51 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 -Item { - id: textFieldWrapper - - property alias font: textField.font - property alias color: textField.color - 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 - 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 - } - } - - 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 + } }