Skip to content

Commit d54b7b1

Browse files
committed
[FEATURE] add new locator filter searching across all layers in display expression
1 parent d6b2537 commit d54b7b1

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed

src/app/locator/qgsinbuiltlocatorfilters.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ void QgsActionLocatorFilter::searchActions( const QString &string, QWidget *pare
191191
}
192192
}
193193

194+
//
195+
// QgsActiveLayerFeaturesLocatorFilter
196+
//
197+
194198
QgsActiveLayerFeaturesLocatorFilter::QgsActiveLayerFeaturesLocatorFilter( QObject *parent )
195199
: QgsLocatorFilter( parent )
196200
{
@@ -298,6 +302,106 @@ void QgsActiveLayerFeaturesLocatorFilter::triggerResult( const QgsLocatorResult
298302
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
299303
}
300304

305+
//
306+
// QgsAllLayersFeaturesLocatorFilter
307+
//
308+
309+
QgsAllLayersFeaturesLocatorFilter::QgsAllLayersFeaturesLocatorFilter( QObject *parent )
310+
: QgsLocatorFilter( parent )
311+
{
312+
setUseWithoutPrefix( false );
313+
}
314+
315+
QgsAllLayersFeaturesLocatorFilter *QgsAllLayersFeaturesLocatorFilter::clone() const
316+
{
317+
return new QgsAllLayersFeaturesLocatorFilter();
318+
}
319+
320+
void QgsAllLayersFeaturesLocatorFilter::prepare( const QString &string, const QgsLocatorContext & )
321+
{
322+
if ( string.length() < 3 )
323+
return;
324+
325+
const QMap<QString, QgsMapLayer *> layers = QgsProject::instance()->mapLayers();
326+
for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
327+
{
328+
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( it.value() );
329+
if ( !layer )
330+
continue;
331+
332+
QgsExpression expression( layer->displayExpression() );
333+
QgsExpressionContext context;
334+
context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
335+
expression.prepare( &context );
336+
337+
QgsFeatureRequest req;
338+
req.setFlags( QgsFeatureRequest::NoGeometry );
339+
req.setFilterExpression( QStringLiteral( "%1 ILIKE '%%2%'" )
340+
.arg( layer->displayExpression() )
341+
.arg( string ) );
342+
req.setLimit( 30 );
343+
344+
PreparedLayer preparedLayer;
345+
preparedLayer.expression = expression;
346+
preparedLayer.context = context;
347+
preparedLayer.layerId = layer->id();
348+
preparedLayer.layerName = layer->name();
349+
preparedLayer.iterator = layer->getFeatures( req );
350+
preparedLayer.layerIcon = QgsMapLayerModel::iconForLayer( layer );
351+
352+
mPreparedLayers.append( preparedLayer );
353+
}
354+
}
355+
356+
void QgsAllLayersFeaturesLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback )
357+
{
358+
int foundInCurrentLayer;
359+
int foundInTotal = 0;
360+
QgsFeature f;
361+
362+
// we cannot used const loop since iterator::nextFeature is not const
363+
for ( PreparedLayer preparedLayer : mPreparedLayers )
364+
{
365+
foundInCurrentLayer = 0;
366+
while ( preparedLayer.iterator.nextFeature( f ) )
367+
{
368+
if ( feedback->isCanceled() )
369+
return;
370+
371+
QgsLocatorResult result;
372+
result.group = preparedLayer.layerName;
373+
374+
preparedLayer.context.setFeature( f );
375+
376+
result.displayString = preparedLayer.expression.evaluate( &( preparedLayer.context ) ).toString();
377+
378+
result.userData = QVariantList() << f.id() << preparedLayer.layerId;
379+
result.icon = preparedLayer.layerIcon;
380+
result.score = static_cast< double >( string.length() ) / result.displayString.size();
381+
emit resultFetched( result );
382+
383+
foundInCurrentLayer++;
384+
foundInTotal++;
385+
if ( foundInCurrentLayer >= mMaxResultsPerLayer )
386+
break;
387+
}
388+
if ( foundInTotal >= mMaxTotalResults )
389+
break;
390+
}
391+
}
392+
393+
void QgsAllLayersFeaturesLocatorFilter::triggerResult( const QgsLocatorResult &result )
394+
{
395+
QVariantList dataList = result.userData.toList();
396+
QgsFeatureId id = dataList.at( 0 ).toLongLong();
397+
QString layerId = dataList.at( 1 ).toString();
398+
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
399+
if ( !layer )
400+
return;
401+
402+
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
403+
}
404+
301405
//
302406
// QgsExpressionCalculatorLocatorFilter
303407
//

src/app/locator/qgsinbuiltlocatorfilters.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,41 @@ class QgsActiveLayerFeaturesLocatorFilter : public QgsLocatorFilter
113113
QIcon mLayerIcon;
114114
};
115115

116+
class QgsAllLayersFeaturesLocatorFilter : public QgsLocatorFilter
117+
{
118+
Q_OBJECT
119+
120+
public:
121+
struct PreparedLayer
122+
{
123+
public:
124+
QgsExpression expression;
125+
QgsExpressionContext context;
126+
QgsFeatureIterator iterator;
127+
QString layerName;
128+
QString layerId;
129+
QIcon layerIcon;
130+
} ;
131+
132+
QgsAllLayersFeaturesLocatorFilter( QObject *parent = nullptr );
133+
QgsAllLayersFeaturesLocatorFilter *clone() const override;
134+
QString name() const override { return QStringLiteral( "allfeatures" ); }
135+
QString displayName() const override { return tr( "Features In All Layers" ); }
136+
Priority priority() const override { return Medium; }
137+
QString prefix() const override { return QStringLiteral( "a" ); }
138+
139+
void prepare( const QString &string, const QgsLocatorContext &context ) override;
140+
void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
141+
void triggerResult( const QgsLocatorResult &result ) override;
142+
143+
private:
144+
int mMaxResultsPerLayer = 6;
145+
int mMaxTotalResults = 12;
146+
QList<PreparedLayer> mPreparedLayers;
147+
148+
149+
};
150+
116151
class APP_EXPORT QgsExpressionCalculatorLocatorFilter : public QgsLocatorFilter
117152
{
118153
Q_OBJECT

src/app/qgisapp.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,6 +3121,7 @@ void QgisApp::createStatusBar()
31213121

31223122
mLocatorWidget->locator()->registerFilter( new QgsActionLocatorFilter( actionObjects ) );
31233123
mLocatorWidget->locator()->registerFilter( new QgsActiveLayerFeaturesLocatorFilter() );
3124+
mLocatorWidget->locator()->registerFilter( new QgsAllLayersFeaturesLocatorFilter() );
31243125
mLocatorWidget->locator()->registerFilter( new QgsExpressionCalculatorLocatorFilter() );
31253126
mLocatorWidget->locator()->registerFilter( new QgsBookmarkLocatorFilter() );
31263127
mLocatorWidget->locator()->registerFilter( new QgsSettingsLocatorFilter() );

src/core/locator/qgslocator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiter
2525
<< QStringLiteral( "layertree" )
2626
<< QStringLiteral( "layouts" )
2727
<< QStringLiteral( "features" )
28+
<< QStringLiteral( "allfeatures" )
2829
<< QStringLiteral( "calculator" )
2930
<< QStringLiteral( "bookmarks" )
3031
<< QStringLiteral( "optionpages" );

0 commit comments

Comments
 (0)