diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ef2c420bd9..3dbb418cf6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -104,6 +104,7 @@ set(QFIELD_CORE_SRCS tracker.cpp trackingmodel.cpp valuemapmodel.cpp + valuemapmodelbase.cpp vertexmodel.cpp viewstatus.cpp) @@ -215,6 +216,7 @@ set(QFIELD_CORE_HDRS tracker.h trackingmodel.h valuemapmodel.h + valuemapmodelbase.h vertexmodel.h viewstatus.h ${CMAKE_CURRENT_BINARY_DIR}/qfield.h) diff --git a/src/core/attributeformmodel.h b/src/core/attributeformmodel.h index c0423216e3..853cebbe75 100644 --- a/src/core/attributeformmodel.h +++ b/src/core/attributeformmodel.h @@ -100,7 +100,7 @@ class AttributeFormModel : public QSortFilterProxyModel */ Q_INVOKABLE QVariant attribute( const QString &name ); - //! Forces the form to update the fields visibility and constraints + //! Applies feature model data such as attribute values, constraints, visibility to the attribute form model Q_INVOKABLE void applyFeatureModel(); //! Applies default values linked to a parent feature diff --git a/src/core/attributeformmodelbase.h b/src/core/attributeformmodelbase.h index c1ce0f1a13..7f25dae670 100644 --- a/src/core/attributeformmodelbase.h +++ b/src/core/attributeformmodelbase.h @@ -28,11 +28,6 @@ class AttributeFormModelBase : public QStandardItemModel { Q_OBJECT - Q_PROPERTY( FeatureModel *featureModel READ featureModel WRITE setFeatureModel NOTIFY featureModelChanged ) - Q_PROPERTY( bool hasTabs READ hasTabs WRITE setHasTabs NOTIFY hasTabsChanged ) - Q_PROPERTY( bool constraintsHardValid READ constraintsHardValid NOTIFY constraintsHardValidChanged ) - Q_PROPERTY( bool constraintsSoftValid READ constraintsSoftValid NOTIFY constraintsSoftValidChanged ) - public: explicit AttributeFormModelBase( QObject *parent = nullptr ); @@ -46,22 +41,26 @@ class AttributeFormModelBase : public QStandardItemModel bool hasTabs() const; void setHasTabs( bool hasTabs ); + //! \copydoc AttributeFormModel::save bool save(); + //! \copydoc AttributeFormModel::create bool create(); + //! \copydoc AttributeFormModel::deleteFeature bool deleteFeature(); bool constraintsHardValid() const; bool constraintsSoftValid() const; + //! \copydoc AttributeFormModel::attribute QVariant attribute( const QString &name ); - //! Applies feature model data such as attribute values, constraints, visibility to the attribute form model + //! \copydoc AttributeFormModel::applyFeatureModel void applyFeatureModel(); - //! Applies default values linked to a parent feature + //! \copydoc AttributeFormModel::applyParentDefaultValues void applyParentDefaultValues(); signals: diff --git a/src/core/multifeaturelistmodelbase.h b/src/core/multifeaturelistmodelbase.h index a1b53dfa34..83cdac639c 100644 --- a/src/core/multifeaturelistmodelbase.h +++ b/src/core/multifeaturelistmodelbase.h @@ -30,25 +30,16 @@ class MultiFeatureListModelBase : public QAbstractItemModel public: explicit MultiFeatureListModelBase( QObject *parent = nullptr ); - /** - * Resets the model to contain features found from a list of \a requests. - */ + //! \copydoc MultiFeatureListModel::setFeatures void setFeatures( const QMap requests ); - /** - * Appends features from a list of \a results. - */ + //! \copydoc MultiFeatureListModel::appendFeatures void appendFeatures( const QList &results ); - /** - * Resets the model to either an empty feature list or one that contains only the selected features. - * \param keepSelected if set to TRUE, selected features will be kept when resetting the model. - */ + //! \copydoc MultiFeatureListModel::clear void clear( const bool keepSelected = false ); - /** - * Empties the list of selected features. - */ + //! \copydoc MultiFeatureListModel::clearSelection void clearSelection(); QHash roleNames() const override; @@ -68,38 +59,28 @@ class MultiFeatureListModelBase : public QAbstractItemModel */ virtual bool removeRows( int row, int count, const QModelIndex &parent ) override; - /** - * Returns the number of features in the model. - */ + //! \copydoc MultiFeatureListModel::count int count() const; - /** - * Returns the number of selected features in the model. - */ + //! \copydoc MultiFeatureListModel::selectedCount int selectedCount() const; - //! Returns TRUE if the selected features can have their attributes value changed + //! \copydoc MultiFeatureListModel::canEditAttributesSelection bool canEditAttributesSelection(); - //! Returns TRUE if the selected features can be merged + //! \copydoc MultiFeatureListModel::canMergeSelection bool canMergeSelection(); - //! Returns TRUE if the selected features can be deleted + //! \copydoc MultiFeatureListModel::canDeleteSelection bool canDeleteSelection(); - //! Returns TRUE if the selected features can be duplicated onto their associated layer + //! \copydoc MultiFeatureListModel::canDuplicateSelection bool canDuplicateSelection(); - //! Returns TRUE if the selected features' geometry can be moved + //! \copydoc MultiFeatureListModel::canMoveSelection bool canMoveSelection(); - /** - * Merges selected features by updating the first seleted feature's geometry - * to a combination (i.e. union) of geometries of all selected features. - * - * All but the first feature will then be removed from the vector layer containing - * the selected features. - */ + //! \copydoc MultiFeatureListModel::mergeSelection bool mergeSelection(); /** @@ -107,41 +88,29 @@ class MultiFeatureListModelBase : public QAbstractItemModel * * \param layer The layer from which a feature will be removed * \param fid The id of the feature to remove + * \param selectionAction */ bool deleteFeature( QgsVectorLayer *layer, QgsFeatureId fid, bool selectionAction = false ); - //! Deletes selected features + //! \copydoc MultiFeatureListModel::deleteSelection bool deleteSelection(); - /** - * Duplicates a feature on a given layer - * - * \param layer The layer within which the feature will be duplicated - * \param feature The feature to be duplicated - */ + //! \copydoc MultiFeatureListModel::duplicateFeature bool duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature ); - //! Duplicates selected features onto their associated layer + //! \copydoc MultiFeatureListModel::duplicateSelection bool duplicateSelection(); - //! Moves selected features along a given \a vector. + //! \copydoc MultiFeatureListModel::moveSelection bool moveSelection( const double x, const double y ); - /** - * Toggles the selection state of a given item. - * \param item the item's row number - */ + //! \copydoc MultiFeatureListModel::toggleSelectedItem void toggleSelectedItem( int item ); - /** - * Returns the list of currently selected features. - * \note the current implementation only allow for selected features from a single layer - */ + //! \copydoc MultiFeatureListModel::selectedFeatures QList selectedFeatures() const; - /** - * Returns the layer containing the list of currently selected features. - */ + //! \copydoc MultiFeatureListModel::selectedLayer QgsVectorLayer *selectedLayer() const; signals: diff --git a/src/core/valuemapmodel.cpp b/src/core/valuemapmodel.cpp index b34e5a7bd9..55da44694f 100644 --- a/src/core/valuemapmodel.cpp +++ b/src/core/valuemapmodel.cpp @@ -16,114 +16,44 @@ * * ***************************************************************************/ -#include "qgsvaluemapfieldformatter.h" #include "valuemapmodel.h" - -#include +#include "valuemapmodelbase.h" ValueMapModel::ValueMapModel( QObject *parent ) - : QAbstractListModel( parent ) -{ -} - -QVariant ValueMapModel::map() const -{ - return mConfiguredMap; -} - -void ValueMapModel::setMap( const QVariant &map ) + : QSortFilterProxyModel( parent ) + , mSourceModel( new ValueMapModelBase( this ) ) { - mMap.clear(); - // QGIS 3 - const QVariantList list = map.toList(); - if ( !list.empty() ) - { - beginInsertRows( QModelIndex(), 0, list.size() ); - - for ( const QVariant &item : list ) - { - const QVariantMap mapItem = item.toMap(); - - const QString key = mapItem.firstKey(); - const QVariant value = mapItem.value( key ); - - mMap.append( qMakePair( value, key ) ); - } - endInsertRows(); - } - else // QGIS 2 compat - { - const QVariantMap valueMap = map.toMap(); - if ( !valueMap.empty() ) - { - beginInsertRows( QModelIndex(), 0, valueMap.size() ); + setSourceModel( mSourceModel ); - QMapIterator i( valueMap ); - while ( i.hasNext() ) - { - i.next(); - const QString key = i.key(); - const QVariant value = i.value(); + setFilterRole( ValueRole ); - mMap.append( qMakePair( value, key ) ); - } - endInsertRows(); - } - } - - mConfiguredMap = map; - emit mapChanged(); -} - -int ValueMapModel::rowCount( const QModelIndex &parent ) const -{ - Q_UNUSED( parent ) - return mMap.size(); + connect( mSourceModel, &ValueMapModelBase::mapChanged, this, &ValueMapModel::mapChanged ); } -QVariant ValueMapModel::data( const QModelIndex &index, int role ) const +QVariant ValueMapModel::map() const { - if ( role == KeyRole ) - return mMap.at( index.row() ).first; - else - return mMap.at( index.row() ).second; + return mSourceModel->map(); } -QHash ValueMapModel::roleNames() const +void ValueMapModel::setMap( const QVariant &map ) { - QHash roles = QAbstractItemModel::roleNames(); - - roles[KeyRole] = "key"; - roles[ValueRole] = "value"; - - return roles; + mSourceModel->setMap( map ); } int ValueMapModel::keyToIndex( const QVariant &key ) const { - int i = 0; - for ( const auto &item : mMap ) - { - if ( item.first.toString() == key.toString() ) - { - return i; - } - ++i; - } - return -1; + return mSourceModel->keyToIndex( key ); } QVariant ValueMapModel::keyForValue( const QString &value ) const { - QVariant result; - - auto match = std::find_if( mMap.begin(), mMap.end(), [&value]( const QPair &x ) { return x.second == value; } ); - - if ( match != mMap.end() ) - result = match->first; + return mSourceModel->keyForValue( value ); +} - if ( result == QgsValueMapFieldFormatter::NULL_VALUE ) - result = QVariant(); +bool ValueMapModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const +{ + QModelIndex index = sourceModel()->index( sourceRow, 0, sourceParent ); - return result; + QVariant data = sourceModel()->data( index, ValueRole ); + return data.toString().contains( filterRegularExpression() ); } diff --git a/src/core/valuemapmodel.h b/src/core/valuemapmodel.h index 336ea19982..4268bafdb2 100644 --- a/src/core/valuemapmodel.h +++ b/src/core/valuemapmodel.h @@ -16,17 +16,18 @@ * * ***************************************************************************/ - #ifndef VALUEMAPMODEL_H #define VALUEMAPMODEL_H -#include -#include +#include "valuemapmodelbase.h" + +#include + /** * A model that manages the key/value pairs for a ValueMap widget. */ -class ValueMapModel : public QAbstractListModel +class ValueMapModel : public QSortFilterProxyModel { Q_OBJECT @@ -39,8 +40,6 @@ class ValueMapModel : public QAbstractListModel */ Q_PROPERTY( QVariant valueMap READ map WRITE setMap NOTIFY mapChanged ) - Q_ENUMS( ValueMapRoles ) - public: //! The roles provided by this model @@ -50,6 +49,8 @@ class ValueMapModel : public QAbstractListModel ValueRole //!< obtain the value }; + Q_ENUM( ValueMapRoles ) + /** * Create a new value map model */ @@ -59,17 +60,12 @@ class ValueMapModel : public QAbstractListModel * The map, see the property description */ QVariant map() const; + /** * The map, see the property description */ void setMap( const QVariant &map ); - int rowCount( const QModelIndex &parent = QModelIndex() ) const override; - - QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; - - QHash roleNames() const override; - /** * Returns the row (index) of a key or -1 if not found. */ @@ -80,6 +76,9 @@ class ValueMapModel : public QAbstractListModel */ Q_INVOKABLE QVariant keyForValue( const QString &value ) const; + protected: + bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override; + signals: /** * Emitted when the map changes. @@ -87,8 +86,7 @@ class ValueMapModel : public QAbstractListModel void mapChanged(); private: - QList> mMap; - QVariant mConfiguredMap; + ValueMapModelBase *mSourceModel = nullptr; }; #endif // VALUEMAPMODEL_H diff --git a/src/core/valuemapmodelbase.cpp b/src/core/valuemapmodelbase.cpp new file mode 100644 index 0000000000..2a6199bfec --- /dev/null +++ b/src/core/valuemapmodelbase.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + valuemapmodelbase.cpp + + ------------------- + begin : March 2019 + copyright : (C) 2019 by Matthias Kuhn + email : matthias@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsvaluemapfieldformatter.h" +#include "valuemapmodel.h" +#include "valuemapmodelbase.h" + +ValueMapModelBase::ValueMapModelBase( QObject *parent ) + : QAbstractListModel( parent ) +{ +} + +QVariant ValueMapModelBase::map() const +{ + return mConfiguredMap; +} + +void ValueMapModelBase::setMap( const QVariant &map ) +{ + mMap.clear(); + // QGIS 3 + const QVariantList list = map.toList(); + if ( !list.empty() ) + { + beginInsertRows( QModelIndex(), 0, list.size() ); + + for ( const QVariant &item : list ) + { + const QVariantMap mapItem = item.toMap(); + + const QString &key = mapItem.firstKey(); + const QVariant value = mapItem.value( key ); + + mMap.append( qMakePair( value, key ) ); + } + endInsertRows(); + } + else // QGIS 2 compat + { + const QVariantMap valueMap = map.toMap(); + if ( !valueMap.empty() ) + { + beginInsertRows( QModelIndex(), 0, valueMap.size() ); + + QMapIterator i( valueMap ); + while ( i.hasNext() ) + { + i.next(); + const QString key = i.key(); + const QVariant value = i.value(); + + mMap.append( qMakePair( value, key ) ); + } + endInsertRows(); + } + } + + mConfiguredMap = map; + emit mapChanged(); +} + +int ValueMapModelBase::rowCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return mMap.size(); +} + +QVariant ValueMapModelBase::data( const QModelIndex &index, int role ) const +{ + if ( index.isValid() == false ) + return QVariant(); + + if ( role == ValueMapModel::KeyRole ) + return mMap.at( index.row() ).first; + + return mMap.at( index.row() ).second; +} + +QHash ValueMapModelBase::roleNames() const +{ + QHash roles = QAbstractItemModel::roleNames(); + + roles[ValueMapModel::KeyRole] = "key"; + roles[ValueMapModel::ValueRole] = "value"; + + return roles; +} + +int ValueMapModelBase::keyToIndex( const QVariant &key ) const +{ + int i = 0; + for ( const auto &item : mMap ) + { + if ( item.first.toString() == key.toString() ) + { + return i; + } + ++i; + } + return -1; +} + +QVariant ValueMapModelBase::keyForValue( const QString &value ) const +{ + QVariant result; + + auto match = std::find_if( mMap.begin(), mMap.end(), [&value]( const QPair &x ) { return x.second == value; } ); + + if ( match != mMap.end() ) + result = match->first; + + if ( result == QgsValueMapFieldFormatter::NULL_VALUE ) + result = QVariant(); + + return result; +} diff --git a/src/core/valuemapmodelbase.h b/src/core/valuemapmodelbase.h new file mode 100644 index 0000000000..492e1468ee --- /dev/null +++ b/src/core/valuemapmodelbase.h @@ -0,0 +1,63 @@ +/*************************************************************************** + valuemapmodelbase.h + + ------------------- + begin : March 2019 + copyright : (C) 2019 by Matthias Kuhn + email : matthias@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef VALUEMAPMODELBASE_H +#define VALUEMAPMODELBASE_H + +#include +#include + +//! \copydoc ValueMapModel +class ValueMapModelBase : public QAbstractListModel +{ + Q_OBJECT + + public: + /** + * Create a new value map model base + */ + explicit ValueMapModelBase( QObject *parent = nullptr ); + + //! \copydoc ValueMapModel::map + QVariant map() const; + + //! \copydoc ValueMapModel::setMap + void setMap( const QVariant &map ); + + //! \copydoc ValueMapModel::keyToIndex + int keyToIndex( const QVariant &key ) const; + + //! \copydoc ValueMapModel::keyForValue + QVariant keyForValue( const QString &value ) const; + + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; + + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + + QHash roleNames() const override; + + signals: + //! \copydoc ValueMapModel::mapChanged + void mapChanged(); + + private: + QList> mMap; + QVariant mConfiguredMap; +}; + +#endif // VALUEMAPMODELBASE_H diff --git a/src/qml/RelationCombobox.qml b/src/qml/RelationCombobox.qml index fde7126b1c..34ea8d4dd2 100644 --- a/src/qml/RelationCombobox.qml +++ b/src/qml/RelationCombobox.qml @@ -114,7 +114,7 @@ Item { featureListModel.searchTerm = searchField.displayText } - Keys.onPressed: { + Keys.onPressed: (event)=> { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { if (featureListModel.rowCount() === 1) { resultsList.itemAtIndex(0).performClick() @@ -267,7 +267,7 @@ Item { anchors.fill: parent propagateComposedEvents: true - onClicked: { + onClicked: (mouse)=> { var item = resultsList.itemAt(resultsList.contentX + mouse.x, resultsList.contentY + mouse.y) if (!item) return; @@ -524,7 +524,7 @@ Item { } property bool isLastKeyPressedReturn: false - Keys.onPressed: { + Keys.onPressed: (event)=> { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { if (!isLastKeyPressedReturn) { applyAutoCompletion() diff --git a/src/qml/editorwidgets/ValueMap.qml b/src/qml/editorwidgets/ValueMap.qml index 9ac3dbec2b..2f5edc17f8 100644 --- a/src/qml/editorwidgets/ValueMap.qml +++ b/src/qml/editorwidgets/ValueMap.qml @@ -1,10 +1,13 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 +import QtQuick.Layouts import org.qfield 1.0 +import org.qgis 1.0 import Theme 1.0 import "." +import ".." EditorWidgetBase { id: valueMap @@ -23,52 +26,56 @@ EditorWidgetBase { height: childrenRect.height enabled: isEnabled - - ComboBox { - id: comboBox + RowLayout { anchors { left: parent.left; right: parent.right } - font: Theme.defaultFont - popup.font: Theme.defaultFont - popup.topMargin: mainWindow.sceneTopMargin - popup.bottomMargin: mainWindow.sceneTopMargin + ComboBox { + id: comboBox + + Layout.fillWidth: true + + font: Theme.defaultFont + + popup.font: Theme.defaultFont + popup.topMargin: mainWindow.sceneTopMargin + popup.bottomMargin: mainWindow.sceneTopMargin - currentIndex: model.keyToIndex(value) + currentIndex: model.keyToIndex(value) - model: ValueMapModel { - id: listModel + model: ValueMapModel { + id: listModel - onMapChanged: { - comboBox.currentIndex = keyToIndex(valueMap.currentKeyValue) + onMapChanged: { + comboBox.currentIndex = keyToIndex(valueMap.currentKeyValue) + } } - } - Component.onCompleted: - { - comboBox.popup.z = 10000 // 1000s are embedded feature forms, use a higher value to insure popups always show above embedded feature formes - model.valueMap = config['map'] - } + Component.onCompleted: + { + comboBox.popup.z = 10000 // 1000s are embedded feature forms, use a higher value to insure popups always show above embedded feature formes + model.valueMap = config['map'] + } - textRole: 'value' + textRole: 'value' - onCurrentTextChanged: { - var key = model.keyForValue(currentText) - valueChangeRequested(key, false) - } + onCurrentTextChanged: { + var key = model.keyForValue(currentText) + valueChangeRequested(key, false) + } - MouseArea { - anchors.fill: parent - propagateComposedEvents: true + MouseArea { + anchors.fill: parent + propagateComposedEvents: true - onClicked: mouse.accepted = false - onPressed: { forceActiveFocus(); mouse.accepted = false; } - onReleased: mouse.accepted = false; - onDoubleClicked: mouse.accepted = false; - onPositionChanged: mouse.accepted = false; - onPressAndHold: mouse.accepted = false; - } + onClicked: mouse.accepted = false + onPressed: (mouse)=> { forceActiveFocus(); mouse.accepted = false; } + onReleased: mouse.accepted = false; + onDoubleClicked: mouse.accepted = false; + onPositionChanged: mouse.accepted = false; + onPressAndHold: mouse.accepted = false; + } - contentItem: Text { + contentItem: Text { leftPadding: enabled ? 5 : 0 text: comboBox.displayText @@ -77,28 +84,235 @@ EditorWidgetBase { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft elide: Text.ElideRight - } + } - background: Item { - implicitWidth: 120 - implicitHeight: 36 + background: Item { + implicitWidth: 120 + implicitHeight: 36 - Rectangle { - visible: !enabled - y: comboBox.height - 12 - width: comboBox.width - height: comboBox.activeFocus ? 2 : 1 - color: comboBox.activeFocus ? Theme.accentColor : Theme.accentLightColor + Rectangle { + visible: !enabled + y: comboBox.height - 12 + width: comboBox.width + height: comboBox.activeFocus ? 2 : 1 + color: comboBox.activeFocus ? Theme.accentColor : Theme.accentLightColor + } + + Rectangle { + visible: enabled + anchors.fill: parent + id: backgroundRect + border.color: comboBox.pressed ? Theme.accentColor : Theme.accentLightColor + border.width: comboBox.visualFocus ? 2 : 1 + color: Theme.controlBackgroundAlternateColor + radius: 2 + } } + } + + FontMetrics { + id: fontMetrics + font: comboBox.font + } + + QfToolButton { + id: searchButton + + Layout.preferredWidth: enabled ? 48 : 0 + Layout.preferredHeight: 48 + + bgcolor: "transparent" + iconSource: Theme.getThemeIcon("ic_baseline_search_black") + iconColor: Theme.mainTextColor - Rectangle { visible: enabled + + onClicked: { + searchFeaturePopup.open() + } + } + + Popup { + id: searchFeaturePopup + + parent: mainWindow.contentItem + x: Theme.popupScreenEdgeMargin + y: Theme.popupScreenEdgeMargin + z: 10000 // 1000s are embedded feature forms, use a higher value to insure feature form popups always show above embedded feature formes + width: parent.width - Theme.popupScreenEdgeMargin * 2 + height: parent.height - Theme.popupScreenEdgeMargin * 2 + padding: 0 + modal: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + focus: visible + + onOpened: { + if (resultsList.contentHeight > resultsList.height) { + searchField.forceActiveFocus() + } + } + + onClosed: { + searchField.text = '' + } + + Page { anchors.fill: parent - id: backgroundRect - border.color: comboBox.pressed ? Theme.accentColor : Theme.accentLightColor - border.width: comboBox.visualFocus ? 2 : 1 - color: Theme.controlBackgroundAlternateColor - radius: 2 + + header: QfPageHeader { + title: fieldLabel + showBackButton: false + showApplyButton: false + showCancelButton: true + onCancel: searchFeaturePopup.close() + } + + TextField { + z: 1 + id: searchField + anchors.left: parent.left + anchors.right: parent.right + + placeholderText: !focus && displayText === '' ? qsTr("Search…") : '' + placeholderTextColor: Theme.mainColor + + height: fontMetrics.height * 2.5 + padding: 24 + bottomPadding: 9 + font: Theme.defaultFont + selectByMouse: true + verticalAlignment: TextInput.AlignVCenter + + inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData + + onDisplayTextChanged: listModel.setFilterFixedString(displayText) + + Keys.onPressed: (event)=> { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + if (listModel.rowCount() === 1) { + resultsList.itemAtIndex(0).performClick() + searchFeaturePopup.close() + } + } + } + } + + QfToolButton { + id: clearButton + z: 1 + width: fontMetrics.height + height: fontMetrics.height + anchors { top: searchField.top; right: searchField.right; topMargin: height - 7; rightMargin: height - 7 } + + padding: 0 + iconSource: Theme.getThemeIcon("ic_clear_black_18dp") + iconColor: Theme.mainTextColor + bgcolor: "transparent" + + opacity: searchField.displayText.length > 0 ? 1 : 0.25 + + onClicked: { + searchField.text = ''; + } + } + + ListView { + id: resultsList + anchors.left: parent.left + anchors.right: parent.right + anchors.top: searchField.bottom + model: listModel + + width: parent.width + height: searchFeaturePopup.height - searchField.height - 50 + clip: true + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded + width: 6 + contentItem: Rectangle { + implicitWidth: 6 + implicitHeight: 25 + color: Theme.mainColor + } + } + + delegate: Rectangle { + id: delegateRect + + property string itemText: value ? value : "NULL" + property bool itemChecked: currentKeyValue == key + + anchors.margins: 10 + height: radioButton.height + width: parent ? parent.width : undefined + color: itemChecked ? Theme.mainColor : searchFeaturePopup.Material ? searchFeaturePopup.Material.dialogColor : Theme.mainBackgroundColor + + RadioButton { + id: radioButton + + anchors.verticalCenter: parent.verticalCenter + width: resultsList.width - padding * 2 + padding: 12 + + font.pointSize: Theme.defaultFont.pointSize + font.weight: itemChecked ? Font.DemiBold : Font.Normal + font.italic: value ? false : true + + checked: itemChecked + indicator: Rectangle {} + + text: searchField.displayText !== '' + ? itemText.replace(new RegExp('('+searchField.displayText+')', "i"), + '$1') + : itemText + + contentItem: Text { + text: parent.text + font: parent.font + width: parent.width + verticalAlignment: Text.AlignVCenter + leftPadding: parent.indicator.width + parent.spacing + elide: Text.ElideRight + color: searchField.displayText !== '' ? Theme.secondaryTextColor : Theme.mainTextColor + textFormat: Text.RichText + } + } + + /* bottom border */ + Rectangle { + anchors.bottom: parent.bottom + height: 1 + color: Theme.controlBorderColor + width: resultsList.width + } + + function performClick() { + if (key === currentKeyValue) + return + + valueChangeRequested(key, false) + searchFeaturePopup.close() + } + } + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + + onClicked: (mouse)=> { + var item = resultsList.itemAt(resultsList.contentX + mouse.x, resultsList.contentY + mouse.y) + if (!item) + return; + + item.performClick() + } + } + + onMovementStarted: { + Qt.inputMethod.hide() + } + } } } }