Skip to content

Commit

Permalink
Implement prefix based locator searching
Browse files Browse the repository at this point in the history
Filters can indicate their preferred search prefix. Searches which
begin with this character will be restricted to the single matching
filter.

E.g. entering 'l buffer' will searching only layers containing 'buffer'

Other prefixes are:
- . search actions
- pl search print layouts
- a search algorithms

Plugins are restricted to a minimum 3 character prefix. We do this
to avoid plugins 'stealing' desirable prefixes, and instead we
want to reserve them for future core filters.
  • Loading branch information
nyalldawson committed May 17, 2017
1 parent b33ce0b commit 102a466
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 4 deletions.
14 changes: 14 additions & 0 deletions python/gui/locator/qgslocatorfilter.sip
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ class QgsLocatorFilter : QObject
:rtype: Priority
%End

virtual QString prefix() const;
%Docstring
Returns the search prefix character(s) for this filter. Prefix a search
with these characters will restrict the locator search to only include
results from this filter.
.. note::

Plugins are not permitted to utilise prefixes with < 3 characters,
as these are reserved for core QGIS functions. If a plugin registers
a filter with a prefix shorter than 3 characters then the prefix will
be ignored.
:rtype: str
%End

virtual void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) = 0;
%Docstring
Retrieves the filter results for a specified search ``string``. The ``context``
Expand Down
3 changes: 3 additions & 0 deletions python/plugins/processing/gui/AlgorithmLocatorFilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ def displayName(self):
def priority(self):
return QgsLocatorFilter.Low

def prefix(self):
return 'a'

def fetchResults(self,string,context,feedback):
for a in QgsApplication.processingRegistry().algorithms():
if feedback.isCanceled():
Expand Down
2 changes: 1 addition & 1 deletion src/app/locator/qgsinbuiltlocatorfilters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void QgsActionLocatorFilter::searchActions( const QString &string, QWidget *pare
continue;
}

if ( !action->isEnabled() || !action->isVisible() )
if ( !action->isEnabled() || !action->isVisible() || action->text().isEmpty() )
continue;
if ( found.contains( action ) )
continue;
Expand Down
3 changes: 3 additions & 0 deletions src/app/locator/qgsinbuiltlocatorfilters.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class QgsLayerTreeLocatorFilter : public QgsLocatorFilter
virtual QString name() const override { return QStringLiteral( "layertree" ); }
virtual QString displayName() const override { return tr( "Project layers" ); }
virtual Priority priority() const override { return Highest; }
QString prefix() const override { return QStringLiteral( "l" ); }

void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;
Expand All @@ -47,6 +48,7 @@ class QgsLayoutLocatorFilter : public QgsLocatorFilter
virtual QString name() const override { return QStringLiteral( "layouts" ); }
virtual QString displayName() const override { return tr( "Project layouts" ); }
virtual Priority priority() const override { return Highest; }
QString prefix() const override { return QStringLiteral( "pl" ); }

void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;
Expand All @@ -63,6 +65,7 @@ class QgsActionLocatorFilter : public QgsLocatorFilter
virtual QString name() const override { return QStringLiteral( "actions" ); }
virtual QString displayName() const override { return tr( "Actions" ); }
virtual Priority priority() const override { return Lowest; }
QString prefix() const override { return QStringLiteral( "." ); }

void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;
Expand Down
36 changes: 33 additions & 3 deletions src/gui/locator/qgslocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ void QgsLocator::deregisterFilter( QgsLocatorFilter *filter )
{
cancelRunningQuery();
mFilters.removeAll( filter );
QString key = mPrefixedFilters.key( filter );
if ( !key.isEmpty() )
mPrefixedFilters.remove( key );
delete filter;
}

Expand All @@ -50,6 +53,20 @@ void QgsLocator::registerFilter( QgsLocatorFilter *filter )
mFilters.append( filter );
filter->setParent( this );
connect( filter, &QgsLocatorFilter::resultFetched, this, &QgsLocator::filterSentResult, Qt::QueuedConnection );

if ( !filter->prefix().isEmpty() )
{
if ( filter->name() == QStringLiteral( "actions" ) || filter->name() == QStringLiteral( "processing_alg" )
|| filter->name() == QStringLiteral( "layertree" ) || filter->name() == QStringLiteral( "layouts" ) )
{
//inbuilt filter, no prefix check
mPrefixedFilters.insert( filter->prefix(), filter );
}
else if ( filter->prefix().length() >= 3 )
{
mPrefixedFilters.insert( filter->prefix(), filter );
}
}
}

void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback )
Expand All @@ -72,13 +89,26 @@ void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c
}
mFeedback = feedback;

auto gatherFilterResults = [string, context, feedback]( QgsLocatorFilter * filter )
mActiveFilters = mFilters;
QString searchString = string;
if ( searchString.indexOf( ' ' ) > 0 )
{
QString prefix = searchString.left( searchString.indexOf( ' ' ) );
if ( mPrefixedFilters.contains( prefix ) )
{
mActiveFilters.clear();
mActiveFilters << mPrefixedFilters.value( prefix );
searchString = searchString.mid( prefix.length() + 1 );
}
}

auto gatherFilterResults = [searchString, context, feedback]( QgsLocatorFilter * filter )
{
if ( !feedback->isCanceled() )
filter->fetchResults( string, context, feedback );
filter->fetchResults( searchString, context, feedback );
};

mFuture = QtConcurrent::map( mFilters, gatherFilterResults );
mFuture = QtConcurrent::map( mActiveFilters, gatherFilterResults );
mFutureWatcher.setFuture( mFuture );
}

Expand Down
2 changes: 2 additions & 0 deletions src/gui/locator/qgslocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ class GUI_EXPORT QgsLocator : public QObject
std::unique_ptr< QgsFeedback > mOwnedFeedback;

QList< QgsLocatorFilter * > mFilters;
QList< QgsLocatorFilter * > mActiveFilters;
QMap< QString, QgsLocatorFilter *> mPrefixedFilters;
QFuture< void > mFuture;
QFutureWatcher< void > mFutureWatcher;

Expand Down
11 changes: 11 additions & 0 deletions src/gui/locator/qgslocatorfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ class GUI_EXPORT QgsLocatorFilter : public QObject
*/
virtual Priority priority() const { return Medium; }

/**
* Returns the search prefix character(s) for this filter. Prefix a search
* with these characters will restrict the locator search to only include
* results from this filter.
* \note Plugins are not permitted to utilise prefixes with < 3 characters,
* as these are reserved for core QGIS functions. If a plugin registers
* a filter with a prefix shorter than 3 characters then the prefix will
* be ignored.
*/
virtual QString prefix() const { return QString(); }

/**
* Retrieves the filter results for a specified search \a string. The \a context
* argument encapsulates the context relating to the search (such as a map
Expand Down

0 comments on commit 102a466

Please sign in to comment.