diff --git a/python/core/locator/qgslocatorcontext.sip.in b/python/core/locator/qgslocatorcontext.sip.in index 3013a2e7322..7c2726356fe 100644 --- a/python/core/locator/qgslocatorcontext.sip.in +++ b/python/core/locator/qgslocatorcontext.sip.in @@ -31,6 +31,8 @@ Constructor for QgsLocatorContext. QgsCoordinateReferenceSystem targetExtentCrs; + bool usingPrefix; + }; diff --git a/python/plugins/processing/gui/AlgorithmLocatorFilter.py b/python/plugins/processing/gui/AlgorithmLocatorFilter.py index c87208b09fc..9e872830b77 100644 --- a/python/plugins/processing/gui/AlgorithmLocatorFilter.py +++ b/python/plugins/processing/gui/AlgorithmLocatorFilter.py @@ -65,7 +65,8 @@ class AlgorithmLocatorFilter(QgsLocatorFilter): if a.flags() & QgsProcessingAlgorithm.FlagHideFromToolbox: continue - if QgsLocatorFilter.stringMatches(a.displayName(), string) or [t for t in a.tags() if QgsLocatorFilter.stringMatches(t, string)]: + if QgsLocatorFilter.stringMatches(a.displayName(), string) or [t for t in a.tags() if QgsLocatorFilter.stringMatches(t, string)] or \ + (context.usingPrefix and not string): result = QgsLocatorResult() result.filter = self result.displayString = a.displayName() diff --git a/src/app/locator/qgsinbuiltlocatorfilters.cpp b/src/app/locator/qgsinbuiltlocatorfilters.cpp index 16d63d9e313..9eb5865891b 100644 --- a/src/app/locator/qgsinbuiltlocatorfilters.cpp +++ b/src/app/locator/qgsinbuiltlocatorfilters.cpp @@ -37,13 +37,13 @@ QgsLayerTreeLocatorFilter *QgsLayerTreeLocatorFilter::clone() const return new QgsLayerTreeLocatorFilter(); } -void QgsLayerTreeLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback * ) +void QgsLayerTreeLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback * ) { QgsLayerTree *tree = QgsProject::instance()->layerTreeRoot(); const QList layers = tree->findLayers(); for ( QgsLayerTreeLayer *layer : layers ) { - if ( layer->layer() && stringMatches( layer->layer()->name(), string ) ) + if ( layer->layer() && ( stringMatches( layer->layer()->name(), string ) || ( context.usingPrefix && string.isEmpty() ) ) ) { QgsLocatorResult result; result.displayString = layer->layer()->name(); @@ -75,12 +75,12 @@ QgsLayoutLocatorFilter *QgsLayoutLocatorFilter::clone() const return new QgsLayoutLocatorFilter(); } -void QgsLayoutLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback * ) +void QgsLayoutLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback * ) { const QList< QgsMasterLayoutInterface * > layouts = QgsProject::instance()->layoutManager()->layouts(); for ( QgsMasterLayoutInterface *layout : layouts ) { - if ( layout && stringMatches( layout->name(), string ) ) + if ( layout && ( stringMatches( layout->name(), string ) || ( context.usingPrefix && string.isEmpty() ) ) ) { QgsLocatorResult result; result.displayString = layout->name(); @@ -332,7 +332,7 @@ QgsBookmarkLocatorFilter *QgsBookmarkLocatorFilter::clone() const return new QgsBookmarkLocatorFilter(); } -void QgsBookmarkLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback ) +void QgsBookmarkLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) { QMap bookmarkMap = QgisApp::instance()->getBookmarkIndexMap(); @@ -346,7 +346,7 @@ void QgsBookmarkLocatorFilter::fetchResults( const QString &string, const QgsLoc QString name = i.key(); - if ( stringMatches( name, string ) ) + if ( stringMatches( name, string ) || ( context.usingPrefix && string.isEmpty() ) ) { QModelIndex index = i.value(); QgsLocatorResult result; diff --git a/src/core/locator/qgslocator.cpp b/src/core/locator/qgslocator.cpp index 4da2a7b4eae..b80af76f880 100644 --- a/src/core/locator/qgslocator.cpp +++ b/src/core/locator/qgslocator.cpp @@ -82,8 +82,9 @@ void QgsLocator::registerFilter( QgsLocatorFilter *filter ) filter->setUseWithoutPrefix( byDefault ); } -void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) +void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c, QgsFeedback *feedback ) { + QgsLocatorContext context( c ); // ideally this should not be required, as well behaved callers // will NOT fire up a new fetchResults call while an existing one is // operating/waiting to be canceled... @@ -112,6 +113,7 @@ void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c activeFilters << mPrefixedFilters.value( prefix ); searchString = searchString.mid( prefix.length() + 1 ); } + context.usingPrefix = !activeFilters.empty(); } if ( activeFilters.isEmpty() ) { diff --git a/src/core/locator/qgslocatorcontext.h b/src/core/locator/qgslocatorcontext.h index 466aa854e3a..b205ca00f38 100644 --- a/src/core/locator/qgslocatorcontext.h +++ b/src/core/locator/qgslocatorcontext.h @@ -51,6 +51,11 @@ class CORE_EXPORT QgsLocatorContext */ QgsCoordinateReferenceSystem targetExtentCrs; + /** + * Will be true if search is being conducted using a filter prefix. + */ + bool usingPrefix = false; + }; #endif // QGSLOCATORCONTEXT_H diff --git a/src/core/locator/qgslocatorfilter.cpp b/src/core/locator/qgslocatorfilter.cpp index 397c0253880..f57954eaa64 100644 --- a/src/core/locator/qgslocatorfilter.cpp +++ b/src/core/locator/qgslocatorfilter.cpp @@ -34,7 +34,7 @@ QgsLocatorFilter::Flags QgsLocatorFilter::flags() const bool QgsLocatorFilter::stringMatches( const QString &candidate, const QString &search ) { - return candidate.contains( search, Qt::CaseInsensitive ); + return !search.isEmpty() && candidate.contains( search, Qt::CaseInsensitive ); } bool QgsLocatorFilter::enabled() const diff --git a/tests/src/app/testqgsapplocatorfilters.cpp b/tests/src/app/testqgsapplocatorfilters.cpp index 88fdae7586d..05580941f59 100644 --- a/tests/src/app/testqgsapplocatorfilters.cpp +++ b/tests/src/app/testqgsapplocatorfilters.cpp @@ -16,6 +16,8 @@ #include "qgisapp.h" #include "qgslocatorfilter.h" #include "qgslocator.h" +#include "qgsprintlayout.h" +#include "qgslayoutmanager.h" #include "locator/qgsinbuiltlocatorfilters.h" #include #include @@ -32,6 +34,8 @@ class TestQgsAppLocatorFilters : public QObject void initTestCase();// will be called before the first testfunction is executed. void cleanupTestCase();// will be called after the last testfunction was executed. void testCalculator(); + void testLayers(); + void testLayouts(); private: QgisApp *mQgisApp = nullptr; @@ -70,6 +74,77 @@ void TestQgsAppLocatorFilters::testCalculator() // invalid expression results = gatherResults( &filter, QStringLiteral( "1+" ), QgsLocatorContext() ); QVERIFY( results.empty() ); +} + +void TestQgsAppLocatorFilters::testLayers() +{ + QgsVectorLayer *l1 = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "aaaaa" ), QStringLiteral( "memory" ) ); + QgsVectorLayer *l2 = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "abc" ), QStringLiteral( "memory" ) ); + QgsVectorLayer *l3 = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "ccccc" ), QStringLiteral( "memory" ) ); + QgsProject::instance()->addMapLayers( QList< QgsMapLayer *>() << l1 << l2 << l3 ); + + QgsLayerTreeLocatorFilter filter; + + QList< QgsLocatorResult > results = gatherResults( &filter, QStringLiteral( "xxxxx" ), QgsLocatorContext() ); + QCOMPARE( results.count(), 0 ); + + results = gatherResults( &filter, QStringLiteral( "aa" ), QgsLocatorContext() ); + QCOMPARE( results.count(), 1 ); + QCOMPARE( results.at( 0 ).userData.toString(), l1->id() ); + + results = gatherResults( &filter, QStringLiteral( "A" ), QgsLocatorContext() ); + QCOMPARE( results.count(), 2 ); + QCOMPARE( results.at( 0 ).userData.toString(), l1->id() ); + QCOMPARE( results.at( 1 ).userData.toString(), l2->id() ); + + results = gatherResults( &filter, QString(), QgsLocatorContext() ); + QCOMPARE( results.count(), 0 ); + + QgsLocatorContext context; + context.usingPrefix = true; + results = gatherResults( &filter, QString(), context ); + QCOMPARE( results.count(), 3 ); + QCOMPARE( results.at( 0 ).userData.toString(), l1->id() ); + QCOMPARE( results.at( 1 ).userData.toString(), l2->id() ); + QCOMPARE( results.at( 2 ).userData.toString(), l3->id() ); +} + +void TestQgsAppLocatorFilters::testLayouts() +{ + QgsPrintLayout *pl1 = new QgsPrintLayout( QgsProject::instance() ); + pl1->setName( QStringLiteral( "aaaaaa" ) ); + QgsProject::instance()->layoutManager()->addLayout( pl1 ); + QgsPrintLayout *pl2 = new QgsPrintLayout( QgsProject::instance() ); + pl2->setName( QStringLiteral( "abc" ) ); + QgsProject::instance()->layoutManager()->addLayout( pl2 ); + QgsPrintLayout *pl3 = new QgsPrintLayout( QgsProject::instance() ); + pl3->setName( QStringLiteral( "ccccc" ) ); + QgsProject::instance()->layoutManager()->addLayout( pl3 ); + + QgsLayoutLocatorFilter filter; + + QList< QgsLocatorResult > results = gatherResults( &filter, QStringLiteral( "xxxxx" ), QgsLocatorContext() ); + QCOMPARE( results.count(), 0 ); + + results = gatherResults( &filter, QStringLiteral( "aa" ), QgsLocatorContext() ); + QCOMPARE( results.count(), 1 ); + QCOMPARE( results.at( 0 ).userData.toString(), pl1->name() ); + + results = gatherResults( &filter, QStringLiteral( "A" ), QgsLocatorContext() ); + QCOMPARE( results.count(), 2 ); + QCOMPARE( results.at( 0 ).userData.toString(), pl1->name() ); + QCOMPARE( results.at( 1 ).userData.toString(), pl2->name() ); + + results = gatherResults( &filter, QString(), QgsLocatorContext() ); + QCOMPARE( results.count(), 0 ); + + QgsLocatorContext context; + context.usingPrefix = true; + results = gatherResults( &filter, QString(), context ); + QCOMPARE( results.count(), 3 ); + QCOMPARE( results.at( 0 ).userData.toString(), pl1->name() ); + QCOMPARE( results.at( 1 ).userData.toString(), pl2->name() ); + QCOMPARE( results.at( 2 ).userData.toString(), pl3->name() ); } @@ -82,7 +157,7 @@ QList TestQgsAppLocatorFilters::gatherResults( QgsLocatorFilte QList< QgsLocatorResult > results; for ( int i = 0; i < spy.count(); ++ i ) { - QVariant v = spy.at( i ).at( i ); + QVariant v = spy.at( i ).at( 0 ); QgsLocatorResult result = v.value(); results.append( result ); } diff --git a/tests/src/python/test_qgslocator.py b/tests/src/python/test_qgslocator.py index b5aac4c8161..b8d9107d0aa 100644 --- a/tests/src/python/test_qgslocator.py +++ b/tests/src/python/test_qgslocator.py @@ -260,6 +260,12 @@ class TestQgsLocator(unittest.TestCase): self.assertEqual(m.data(m.index(2, 0)), 'a1') self.assertEqual(m.data(m.index(3, 0)), 'a2') + def testStringMatches(self): + self.assertFalse(QgsLocatorFilter.stringMatches('xxx', 'yyyy')) + self.assertTrue(QgsLocatorFilter.stringMatches('axxxy', 'xxx')) + self.assertTrue(QgsLocatorFilter.stringMatches('aXXXXy', 'xxx')) + self.assertFalse(QgsLocatorFilter.stringMatches('aXXXXy', '')) + if __name__ == '__main__': unittest.main()