diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7b7c5fc490..aa6535937a 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/valuemapmodel.cpp b/src/core/valuemapmodel.cpp index b34e5a7bd9..fd7e46a0f5 100644 --- a/src/core/valuemapmodel.cpp +++ b/src/core/valuemapmodel.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - valuemapmodel.cpp + valuemapmodelbase.cpp ------------------- begin : March 2019 @@ -16,114 +16,36 @@ * * ***************************************************************************/ -#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; - - if ( result == QgsValueMapFieldFormatter::NULL_VALUE ) - result = QVariant(); - - return result; + return mSourceModel->keyForValue( value ); } diff --git a/src/core/valuemapmodel.h b/src/core/valuemapmodel.h index 336ea19982..cfb2fe093f 100644 --- a/src/core/valuemapmodel.h +++ b/src/core/valuemapmodel.h @@ -16,17 +16,15 @@ * * ***************************************************************************/ - #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 +37,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,26 +46,20 @@ class ValueMapModel : public QAbstractListModel ValueRole //!< obtain the value }; - /** - * Create a new value map model - */ + Q_ENUM( ValueMapRoles ) + explicit ValueMapModel( QObject *parent = nullptr ); /** * 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. */ @@ -87,8 +77,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..64a6d3eac4 --- /dev/null +++ b/src/core/valuemapmodelbase.cpp @@ -0,0 +1,130 @@ +/*************************************************************************** + 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" + +#include + +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 ( role == ValueMapModel::KeyRole ) + return mMap.at( index.row() ).first; + else + 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..ad60aca31d --- /dev/null +++ b/src/core/valuemapmodelbase.h @@ -0,0 +1,84 @@ +/*************************************************************************** + 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 + +/** + * A model that manages the key/value pairs for a ValueMap widget. + */ +class ValueMapModelBase : public QAbstractListModel +{ + Q_OBJECT + + /** + * A list of QVariantMap, wrapped in a QVariant. + * + * Like this: + * + * [{'CH': 'Switzerland'}, {'DE': 'Germany'}, {'FR': 'France'}] + */ + Q_PROPERTY( QVariant valueMap READ map WRITE setMap NOTIFY mapChanged ) + + public: + /** + * Create a new value map model + */ + explicit ValueMapModelBase( QObject *parent = nullptr ); + + /** + * 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. + */ + Q_INVOKABLE int keyToIndex( const QVariant &key ) const; + + /** + * Returns the key for a value or an invalid QVariant if not found. + */ + Q_INVOKABLE QVariant keyForValue( const QString &value ) const; + + signals: + /** + * Emitted when the map changes. + */ + void mapChanged(); + + private: + QList> mMap; + QVariant mConfiguredMap; +}; + +#endif // VALUEMAPMODELBASE_H diff --git a/src/qml/editorwidgets/ValueMap.qml b/src/qml/editorwidgets/ValueMap.qml index 9ac3dbec2b..218f133fc4 100644 --- a/src/qml/editorwidgets/ValueMap.qml +++ b/src/qml/editorwidgets/ValueMap.qml @@ -1,5 +1,6 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 +import QtQuick.Layouts import org.qfield 1.0 import Theme 1.0 @@ -29,10 +30,6 @@ EditorWidgetBase { anchors { left: parent.left; right: parent.right } font: Theme.defaultFont - popup.font: Theme.defaultFont - popup.topMargin: mainWindow.sceneTopMargin - popup.bottomMargin: mainWindow.sceneTopMargin - currentIndex: model.keyToIndex(value) model: ValueMapModel { @@ -101,5 +98,45 @@ EditorWidgetBase { radius: 2 } } + + popup: Popup { + y: comboBox.height - 1 + width: comboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + font: Theme.defaultFont + topMargin: mainWindow.sceneTopMargin + bottomMargin: mainWindow.sceneTopMargin + + contentItem: + ColumnLayout { + anchors.fill: parent + TextField { + id: textInputFilter + placeholderText: qsTr("Filter") + Layout.preferredHeight: valueMap.height + Layout.fillWidth: true + + onTextChanged: listModel.setFilterFixedString(text) + } + + ListView { + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + implicitHeight: contentHeight + model: comboBox.popup.visible ? comboBox.delegateModel : null + currentIndex: comboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + } + + background: Rectangle { + border.color: "#21be2b" + radius: 2 + } + } } }