Skip to content

Commit

Permalink
Merge pull request #5140 from opengisch/value_relation_group
Browse files Browse the repository at this point in the history
Grouping functionality for the value relation editor widget
  • Loading branch information
nirvn authored Mar 26, 2024
2 parents c14ccb8 + 2b40849 commit 371e645
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 26 deletions.
68 changes: 58 additions & 10 deletions src/core/featurelistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,20 @@ int FeatureListModel::columnCount( const QModelIndex &parent ) const

QVariant FeatureListModel::data( const QModelIndex &index, int role ) const
{
if ( role == Qt::DisplayRole || role == DisplayStringRole )
{
return mEntries.value( index.row() ).displayString;
}
else if ( role == KeyFieldRole )
if ( index.row() < 0 || index.row() >= mEntries.size() )
return QVariant();

switch ( role )
{
return mEntries.value( index.row() ).key;
case Qt::DisplayRole:
case DisplayStringRole:
return mEntries.value( index.row() ).displayString;

case KeyFieldRole:
return mEntries.value( index.row() ).key;

case GroupFieldRole:
return mEntries.value( index.row() ).group;
}

return QVariant();
Expand All @@ -98,6 +105,7 @@ QHash<int, QByteArray> FeatureListModel::roleNames() const

roles[KeyFieldRole] = "keyFieldValue";
roles[DisplayStringRole] = "displayString";
roles[GroupFieldRole] = "groupFieldValue";

return roles;
}
Expand Down Expand Up @@ -167,6 +175,38 @@ void FeatureListModel::setDisplayValueField( const QString &displayValueField )
emit displayValueFieldChanged();
}

QString FeatureListModel::groupField() const
{
return mGroupField;
}

void FeatureListModel::setGroupField( const QString &groupField )
{
if ( mGroupField == groupField )
return;

mGroupField = groupField;

reloadLayer();

emit groupFieldChanged();
}

bool FeatureListModel::displayGroupName() const
{
return mDisplayGroupName;
}

void FeatureListModel::setDisplayGroupName( bool displayGroupName )
{
if ( mDisplayGroupName == displayGroupName )
return;

mDisplayGroupName = displayGroupName;

emit displayGroupNameChanged();
}

int FeatureListModel::findKey( const QVariant &key ) const
{
int idx = 0;
Expand Down Expand Up @@ -255,6 +295,9 @@ void FeatureListModel::gatherFeatureList()
if ( !keyField().isNull() )
referencedColumns << mKeyField;

if ( !groupField().isNull() )
referencedColumns << mGroupField;

referencedColumns << mDisplayValueField;

QgsFields fields = mCurrentLayer->fields();
Expand Down Expand Up @@ -303,7 +346,7 @@ void FeatureListModel::gatherFeatureList()

cleanupGatherer();

mGatherer = new FeatureExpressionValuesGatherer( mCurrentLayer, fieldDisplayString, request, QStringList() << keyField() );
mGatherer = new FeatureExpressionValuesGatherer( mCurrentLayer, fieldDisplayString, request, QStringList() << keyField() << groupField() );
connect( mGatherer, &QThread::finished, this, &FeatureListModel::processFeatureList );
mGatherer->start();
}
Expand All @@ -318,7 +361,7 @@ void FeatureListModel::processFeatureList()
QList<Entry> entries;

if ( mAddNull )
entries.append( Entry( QStringLiteral( "<i>NULL</i>" ), QVariant(), QgsFeatureId() ) );
entries.append( Entry( QStringLiteral( "<i>NULL</i>" ), QVariant(), QVariant(), QgsFeatureId() ) );

const QVector<FeatureExpressionValuesGatherer::Entry> gatheredEntries = mGatherer->entries();
mGatherer->deleteLater();
Expand All @@ -328,7 +371,7 @@ void FeatureListModel::processFeatureList()
{
Entry entry;

entry = Entry( gatheredEntry.value, gatheredEntry.identifierFields.at( 0 ), gatheredEntry.featureId );
entry = Entry( gatheredEntry.value, gatheredEntry.identifierFields.at( 0 ), gatheredEntry.identifierFields.at( 1 ), gatheredEntry.featureId );

if ( !mSearchTerm.isEmpty() )
{
Expand All @@ -340,7 +383,7 @@ void FeatureListModel::processFeatureList()
entries.append( entry );
}

if ( mOrderByValue || !mSearchTerm.isEmpty() )
if ( mOrderByValue || !mGroupField.isEmpty() || !mSearchTerm.isEmpty() )
{
std::sort( entries.begin(), entries.end(), [=]( const Entry &entry1, const Entry &entry2 ) {
if ( entry1.key.isNull() )
Expand All @@ -349,6 +392,11 @@ void FeatureListModel::processFeatureList()
if ( entry2.key.isNull() )
return false;

if ( !mGroupField.isEmpty() && entry1.group != entry2.group )
{
return entry1.group < entry2.group;
}

if ( !mSearchTerm.isEmpty() )
{
const bool entry1StartsWithSearchTerm = entry1.displayString.toLower().startsWith( mSearchTerm.toLower() );
Expand Down
31 changes: 28 additions & 3 deletions src/core/featurelistmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,34 @@ class FeatureListModel : public QAbstractItemModel
* The vector layer to list
*/
Q_PROPERTY( QgsVectorLayer *currentLayer READ currentLayer WRITE setCurrentLayer NOTIFY currentLayerChanged )

/**
* The primary key field
*/
Q_PROPERTY( QString keyField READ keyField WRITE setKeyField NOTIFY keyFieldChanged )

/**
* The display value field
*/
Q_PROPERTY( QString displayValueField READ displayValueField WRITE setDisplayValueField NOTIFY displayValueFieldChanged )

/**
* Whether the features should be ordered by value
* The grouping key field
*/
Q_PROPERTY( QString groupField READ groupField WRITE setGroupField NOTIFY groupFieldChanged )

/**
* Set to TRUE if the group name will be displayed in the list
*/
Q_PROPERTY( bool displayGroupName READ displayGroupName WRITE setDisplayGroupName NOTIFY displayGroupNameChanged )

/**
* Set to TRUE if features should be ordered by value
*/
Q_PROPERTY( bool orderByValue READ orderByValue WRITE setOrderByValue NOTIFY orderByValueChanged )

/**
* Whether a null values is allowed in the list
* Set to TRUE if null values are allowed in the list
*/
Q_PROPERTY( bool addNull READ addNull WRITE setAddNull NOTIFY addNullChanged )

Expand All @@ -81,6 +93,7 @@ class FeatureListModel : public QAbstractItemModel
{
KeyFieldRole = Qt::UserRole + 1,
DisplayStringRole,
GroupFieldRole,
};

Q_ENUM( FeatureListRoles )
Expand Down Expand Up @@ -112,6 +125,12 @@ class FeatureListModel : public QAbstractItemModel
QString displayValueField() const;
void setDisplayValueField( const QString &displayValueField );

QString groupField() const;
void setGroupField( const QString &groupField );

bool displayGroupName() const;
void setDisplayGroupName( bool displayGroupName );

/**
* Get the row for a given key value.
*/
Expand Down Expand Up @@ -176,6 +195,8 @@ class FeatureListModel : public QAbstractItemModel
void currentLayerChanged();
void keyFieldChanged();
void displayValueFieldChanged();
void groupFieldChanged();
void displayGroupNameChanged();
void orderByValueChanged();
void addNullChanged();
void filterExpressionChanged();
Expand All @@ -198,9 +219,10 @@ class FeatureListModel : public QAbstractItemModel
private:
struct Entry
{
Entry( const QString &displayString, const QVariant &key, const QgsFeatureId &fid )
Entry( const QString &displayString, const QVariant &key, const QVariant &group, const QgsFeatureId &fid )
: displayString( displayString )
, key( key )
, group( group )
, fid( fid )
, fuzzyScore( 0 )
{}
Expand All @@ -215,6 +237,7 @@ class FeatureListModel : public QAbstractItemModel

QString displayString;
QVariant key;
QVariant group;
QgsFeatureId fid;
double fuzzyScore;
};
Expand All @@ -237,6 +260,8 @@ class FeatureListModel : public QAbstractItemModel
QList<Entry> mEntries;
QString mKeyField;
QString mDisplayValueField;
QString mGroupField;
bool mDisplayGroupName = false;
bool mOrderByValue = false;
bool mAddNull = false;
QString mFilterExpression;
Expand Down
81 changes: 68 additions & 13 deletions src/qml/RelationCombobox.qml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Item {
anchors.left: parent.left
anchors.right: parent.right

placeholderText: displayText == '' ? qsTr("Search…") : ''
placeholderText: !focus && displayText == '' ? qsTr("Search…") : ''
placeholderTextColor: Theme.mainColor

height: fontMetrics.height * 2.5
Expand Down Expand Up @@ -163,6 +163,25 @@ Item {
}
}

section.property: featureListModel.groupField != "" ? "groupFieldValue" : ""
section.labelPositioning: ViewSection.CurrentLabelAtStart | ViewSection.InlineLabels
section.delegate: Component {
Rectangle {
width:parent.width
height: featureListModel.displayGroupName ? 30 : 5
color: Theme.controlBackgroundAlternateColor

Text {
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
font.bold: true
font.pointSize: Theme.resultFont.pointSize
color: Theme.mainTextColor
text: section
visible: featureListModel.displayGroupName
}
}
}

delegate: Rectangle {
id: delegateRect

Expand All @@ -175,7 +194,7 @@ Item {
anchors.margins: 10
height: radioButton.visible ? radioButton.height : checkBoxButton.height
width: parent ? parent.width : undefined
color: model.checked ? Theme.mainColor : Theme.controlBackgroundAlternateColor
color: model.checked ? Theme.mainColor : searchFeaturePopup.Material ? searchFeaturePopup.Material.dialogColor : Theme.mainBackgroundColor

Row {
RadioButton {
Expand Down Expand Up @@ -302,25 +321,22 @@ Item {
anchors.fill: parent
propagateComposedEvents: true

onClicked: { mouse.accepted = false; }
onPressed: {
forceActiveFocus();
mouse.accepted = false;
onClicked: (mouse) => { 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;
onReleased: (mouse) => { mouse.accepted = false }
onDoubleClicked: (mouse) => { mouse.accepted = false }
onPositionChanged: (mouse) => { mouse.accepted = false }
onPressAndHold: (mouse) => { mouse.accepted = false }
}

Component.onCompleted: {
comboBox.popup.z = 10000 // 1000s are embedded feature forms, use a higher value to insure popups always show above embedded feature formes
}

font: Theme.defaultFont
popup.font: Theme.defaultFont
popup.topMargin: mainWindow.sceneTopMargin
popup.bottomMargin: mainWindow.sceneTopMargin

contentItem: Text {
leftPadding: enabled ? 5 : 0
Expand All @@ -335,6 +351,45 @@ Item {
elide: Text.ElideRight
}

popup: Popup {
y: comboBox.height - 1
width: comboBox.width
implicitHeight: contentItem.implicitHeight
padding: 1
font: Theme.defaultFont
topMargin: mainWindow.sceneTopMargin
bottomMargin: mainWindow.sceneTopMargin

contentItem: ListView {
clip: true
implicitHeight: Math.min(mainWindow.height - mainWindow.sceneTopMargin - mainWindow.sceneTopMargin, contentHeight)
model: comboBox.popup.visible ? comboBox.delegateModel : null
currentIndex: comboBox.highlightedIndex

section.property: featureListModel.groupField != "" ? "groupFieldValue" : ""
section.labelPositioning: ViewSection.CurrentLabelAtStart | ViewSection.InlineLabels
section.delegate: Component {
Rectangle {
width:parent.width
height: featureListModel.displayGroupName ? 30 : 5
color: Theme.mainBackgroundColor

Text {
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
font.bold: true
font.pointSize: Theme.resultFont.pointSize
color: Theme.mainTextColor
text: section
visible: featureListModel.displayGroupName
}
}
}

ScrollIndicator.vertical: ScrollIndicator { }
}
}


background: Item {
implicitWidth: 120
implicitHeight: 36
Expand Down
2 changes: 2 additions & 0 deletions src/qml/editorwidgets/ValueRelation.qml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ EditorWidgetBase {
currentFormFeature: currentFeature
keyField: config['Key']
displayValueField: config['Value']
groupField: config['Group']
displayGroupName: config['DisplayGroupName']
addNull: config['AllowNull']
orderByValue: config['OrderByValue']
filterExpression: config['FilterExpression']
Expand Down

1 comment on commit 371e645

@qfield-fairy
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.