mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-16 00:05:45 -04:00
[FEATURE] enable auto completion in locator
a locator filter can now return a completion list while preparing the search the line edit will use the first matching completion and display it as light grey text the completion can be triggered by pressing Tab key
This commit is contained in:
parent
014de9614d
commit
002a7033f0
@ -131,6 +131,14 @@ Returns ``True`` if a query is currently being executed by the locator.
|
||||
Will call clearPreviousResults on all filters
|
||||
|
||||
.. versionadded:: 3.2
|
||||
%End
|
||||
|
||||
QStringList completionList() const;
|
||||
%Docstring
|
||||
Returns the list for auto completion
|
||||
This list is updated when preparing the search
|
||||
|
||||
.. versionadded:: 3.16
|
||||
%End
|
||||
|
||||
signals:
|
||||
@ -139,6 +147,21 @@ Will call clearPreviousResults on all filters
|
||||
%Docstring
|
||||
Emitted whenever a filter encounters a matching ``result`` after the :py:func:`~QgsLocator.fetchResults` method
|
||||
is called.
|
||||
%End
|
||||
|
||||
void searchBegan();
|
||||
%Docstring
|
||||
Emitted when locator has begun a search, before actualy preparing it.
|
||||
|
||||
.. versionadded:: 3.16
|
||||
%End
|
||||
|
||||
void searchPrepared();
|
||||
%Docstring
|
||||
Emitted when locator has prepared the search (:py:func:`QgsLocatorFilter.prepare`)
|
||||
before the search is actually performed
|
||||
|
||||
.. versionadded:: 3.16
|
||||
%End
|
||||
|
||||
void finished();
|
||||
|
@ -163,12 +163,13 @@ results from this filter.
|
||||
.. seealso:: :py:func:`activePrefix`
|
||||
%End
|
||||
|
||||
virtual void prepare( const QString &string, const QgsLocatorContext &context );
|
||||
virtual QStringList prepare( const QString &string, const QgsLocatorContext &context );
|
||||
%Docstring
|
||||
Prepares the filter instance for an upcoming search for the specified ``string``. This method is always called
|
||||
from the main thread, and individual filter subclasses should perform whatever
|
||||
tasks are required in order to allow a subsequent search to safely execute
|
||||
on a background thread.
|
||||
The method return an autocompletion list
|
||||
%End
|
||||
|
||||
virtual void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) = 0;
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsLocatorWidget : QWidget
|
||||
{
|
||||
%Docstring
|
||||
|
@ -124,6 +124,9 @@ void QgsLocator::registerFilter( QgsLocatorFilter *filter )
|
||||
|
||||
void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c, QgsFeedback *feedback )
|
||||
{
|
||||
mAutocompleList.clear();
|
||||
emit searchBegan();
|
||||
|
||||
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
|
||||
@ -182,7 +185,15 @@ void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c
|
||||
result.filter = filter;
|
||||
filterSentResult( result );
|
||||
} );
|
||||
clone->prepare( searchString, context );
|
||||
QStringList autoCompleteList = clone->prepare( searchString, context );
|
||||
if ( context.usingPrefix )
|
||||
{
|
||||
for ( int i = 0; i < autoCompleteList.length(); i++ )
|
||||
{
|
||||
autoCompleteList[i].prepend( QStringLiteral( "%1 " ).arg( prefix ) );
|
||||
}
|
||||
}
|
||||
mAutocompleList.append( autoCompleteList );
|
||||
|
||||
if ( clone->flags() & QgsLocatorFilter::FlagFast )
|
||||
{
|
||||
@ -191,7 +202,6 @@ void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c
|
||||
}
|
||||
else
|
||||
{
|
||||
// run filter in background
|
||||
threadedFilters.append( clone.release() );
|
||||
}
|
||||
}
|
||||
@ -220,6 +230,8 @@ void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c
|
||||
thread->start();
|
||||
}
|
||||
|
||||
emit searchPrepared();
|
||||
|
||||
if ( mActiveThreads.empty() )
|
||||
emit finished();
|
||||
}
|
||||
|
@ -145,6 +145,13 @@ class CORE_EXPORT QgsLocator : public QObject
|
||||
*/
|
||||
void clearPreviousResults();
|
||||
|
||||
/**
|
||||
* Returns the list for auto completion
|
||||
* This list is updated when preparing the search
|
||||
* \since QGIS 3.16
|
||||
*/
|
||||
QStringList completionList() const {return mAutocompleList;}
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
@ -153,6 +160,19 @@ class CORE_EXPORT QgsLocator : public QObject
|
||||
*/
|
||||
void foundResult( const QgsLocatorResult &result );
|
||||
|
||||
/**
|
||||
* Emitted when locator has begun a search, before actualy preparing it.
|
||||
* \since QGIS 3.16
|
||||
*/
|
||||
void searchBegan();
|
||||
|
||||
/**
|
||||
* Emitted when locator has prepared the search (\see QgsLocatorFilter::prepare)
|
||||
* before the search is actually performed
|
||||
* \since QGIS 3.16
|
||||
*/
|
||||
void searchPrepared();
|
||||
|
||||
/**
|
||||
* Emitted when locator has finished a query, either as a result
|
||||
* of successful completion or early cancellation.
|
||||
@ -171,6 +191,8 @@ class CORE_EXPORT QgsLocator : public QObject
|
||||
QList< QgsLocatorFilter * > mFilters;
|
||||
QList< QThread * > mActiveThreads;
|
||||
|
||||
QStringList mAutocompleList;
|
||||
|
||||
void cancelRunningQuery();
|
||||
|
||||
};
|
||||
|
@ -219,8 +219,9 @@ class CORE_EXPORT QgsLocatorFilter : public QObject
|
||||
* from the main thread, and individual filter subclasses should perform whatever
|
||||
* tasks are required in order to allow a subsequent search to safely execute
|
||||
* on a background thread.
|
||||
* The method return an autocompletion list
|
||||
*/
|
||||
virtual void prepare( const QString &string, const QgsLocatorContext &context ) { Q_UNUSED( string ) Q_UNUSED( context ); }
|
||||
virtual QStringList prepare( const QString &string, const QgsLocatorContext &context ) { Q_UNUSED( string ) Q_UNUSED( context ); return QStringList();}
|
||||
|
||||
/**
|
||||
* Retrieves the filter results for a specified search \a string. The \a context
|
||||
|
@ -24,14 +24,17 @@
|
||||
#include "qgsapplication.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsguiutils.h"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QCompleter>
|
||||
#include <QMenu>
|
||||
#include <QTextLayout>
|
||||
#include <QTextLine>
|
||||
|
||||
QgsLocatorWidget::QgsLocatorWidget( QWidget *parent )
|
||||
: QWidget( parent )
|
||||
, mModelBridge( new QgsLocatorModelBridge( this ) )
|
||||
, mLineEdit( new QgsFilterLineEdit() )
|
||||
, mLineEdit( new QgsLocatorLineEdit( this ) )
|
||||
, mResultsView( new QgsLocatorResultsView() )
|
||||
{
|
||||
mLineEdit->setShowClearButton( true );
|
||||
@ -85,7 +88,7 @@ QgsLocatorWidget::QgsLocatorWidget( QWidget *parent )
|
||||
|
||||
connect( mModelBridge, &QgsLocatorModelBridge::resultAdded, this, &QgsLocatorWidget::resultAdded );
|
||||
connect( mModelBridge, &QgsLocatorModelBridge::isRunningChanged, this, [ = ]() {mLineEdit->setShowSpinner( mModelBridge->isRunning() );} );
|
||||
connect( mModelBridge, & QgsLocatorModelBridge::resultsCleared, this, [ = ]() {mHasSelectedResult = false;} );
|
||||
connect( mModelBridge, &QgsLocatorModelBridge::resultsCleared, this, [ = ]() {mHasSelectedResult = false;} );
|
||||
|
||||
// have a tiny delay between typing text in line edit and showing the window
|
||||
mPopupTimer.setInterval( 100 );
|
||||
@ -260,8 +263,11 @@ bool QgsLocatorWidget::eventFilter( QObject *obj, QEvent *event )
|
||||
mResultsContainer->hide();
|
||||
return true;
|
||||
case Qt::Key_Tab:
|
||||
mHasSelectedResult = true;
|
||||
mResultsView->selectNextResult();
|
||||
if ( !mLineEdit->performCompletion() )
|
||||
{
|
||||
mHasSelectedResult = true;
|
||||
mResultsView->selectNextResult();
|
||||
}
|
||||
return true;
|
||||
case Qt::Key_Backtab:
|
||||
mHasSelectedResult = true;
|
||||
@ -330,7 +336,6 @@ void QgsLocatorWidget::configMenuAboutToShow()
|
||||
}
|
||||
|
||||
|
||||
|
||||
void QgsLocatorWidget::acceptCurrentEntry()
|
||||
{
|
||||
if ( mModelBridge->hasQueueRequested() )
|
||||
@ -352,8 +357,6 @@ void QgsLocatorWidget::acceptCurrentEntry()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
//
|
||||
@ -449,5 +452,75 @@ void QgsLocatorFilterFilter::triggerResult( const QgsLocatorResult &result )
|
||||
mLocator->search( result.userData.toString() );
|
||||
}
|
||||
|
||||
QgsLocatorLineEdit::QgsLocatorLineEdit( QgsLocatorWidget *locator, QWidget *parent )
|
||||
: QgsFilterLineEdit( parent )
|
||||
, mLocatorWidget( locator )
|
||||
{
|
||||
connect( mLocatorWidget->locator(), &QgsLocator::searchPrepared, this, [&] { update(); } );
|
||||
}
|
||||
|
||||
void QgsLocatorLineEdit::paintEvent( QPaintEvent *event )
|
||||
{
|
||||
// this adds the completion as grey text at the right of the cursor
|
||||
// see https://stackoverflow.com/a/50425331/1548052
|
||||
// this is possible that the completion might be badly rendered if the cursor is larger than the line edit
|
||||
// this sounds acceptable as it is not very likely to use completion for super long texts
|
||||
// for more details see https://stackoverflow.com/a/54218192/1548052
|
||||
|
||||
QLineEdit::paintEvent( event );
|
||||
|
||||
if ( !hasFocus() )
|
||||
return;
|
||||
|
||||
QString currentText = text();
|
||||
|
||||
if ( currentText.length() == 0 || cursorPosition() < currentText.length() )
|
||||
return;
|
||||
|
||||
const QStringList completionList = mLocatorWidget->locator()->completionList();
|
||||
|
||||
QString completion;
|
||||
for ( const QString &candidate : completionList )
|
||||
{
|
||||
if ( candidate.startsWith( currentText ) )
|
||||
{
|
||||
completion = candidate.right( candidate.length() - currentText.length() );
|
||||
mCompletionText = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( completion.isEmpty() )
|
||||
return;
|
||||
|
||||
ensurePolished(); // ensure font() is up to date
|
||||
|
||||
QRect cr = cursorRect();
|
||||
QPoint pos = cr.topRight() - QPoint( cr.width() / 2, 0 );
|
||||
|
||||
QTextLayout l( completion, font() );
|
||||
l.beginLayout();
|
||||
QTextLine line = l.createLine();
|
||||
line.setLineWidth( width() - pos.x() );
|
||||
line.setPosition( pos );
|
||||
l.endLayout();
|
||||
|
||||
QPainter p( this );
|
||||
p.setPen( QPen( Qt::gray, 1 ) );
|
||||
l.draw( &p, QPoint( 0, 0 ) );
|
||||
}
|
||||
|
||||
bool QgsLocatorLineEdit::performCompletion()
|
||||
{
|
||||
if ( !mCompletionText.isEmpty() )
|
||||
{
|
||||
setText( mCompletionText );
|
||||
mCompletionText.clear();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
///@endcond
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include "qgis_gui.h"
|
||||
#include "qgslocatorfilter.h"
|
||||
#include "qgsfloatingwidget.h"
|
||||
#include "qgsfilterlineedit.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTreeView>
|
||||
#include <QFocusEvent>
|
||||
@ -28,10 +30,10 @@
|
||||
#include <QTimer>
|
||||
|
||||
class QgsLocator;
|
||||
class QgsFilterLineEdit;
|
||||
class QgsLocatorResultsView;
|
||||
class QgsMapCanvas;
|
||||
class QgsLocatorModelBridge;
|
||||
class QgsLocatorLineEdit;
|
||||
|
||||
/**
|
||||
* \class QgsLocatorWidget
|
||||
@ -98,7 +100,7 @@ class GUI_EXPORT QgsLocatorWidget : public QWidget
|
||||
|
||||
private:
|
||||
QgsLocatorModelBridge *mModelBridge = nullptr;
|
||||
QgsFilterLineEdit *mLineEdit = nullptr;
|
||||
QgsLocatorLineEdit *mLineEdit = nullptr;
|
||||
QgsFloatingWidget *mResultsContainer = nullptr;
|
||||
QgsLocatorResultsView *mResultsView = nullptr;
|
||||
QgsMapCanvas *mMapCanvas = nullptr;
|
||||
@ -110,6 +112,7 @@ class GUI_EXPORT QgsLocatorWidget : public QWidget
|
||||
bool mHasSelectedResult = false;
|
||||
|
||||
void acceptCurrentEntry();
|
||||
|
||||
};
|
||||
|
||||
#ifndef SIP_RUN
|
||||
@ -171,6 +174,30 @@ class GUI_EXPORT QgsLocatorResultsView : public QTreeView
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class QgsLocatorLineEdit
|
||||
* \ingroup gui
|
||||
* Custom line edit to handle completion within the line edit as a light gray text
|
||||
* \since QGIS 3.16
|
||||
*/
|
||||
class QgsLocatorLineEdit : public QgsFilterLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QgsLocatorLineEdit( QgsLocatorWidget *locator, QWidget *parent = nullptr );
|
||||
|
||||
//! Perform completion and returns true if successful
|
||||
bool performCompletion();
|
||||
|
||||
protected:
|
||||
void paintEvent( QPaintEvent *event ) override;
|
||||
|
||||
private:
|
||||
QgsLocatorWidget *mLocatorWidget = nullptr;
|
||||
QString mCompletionText = nullptr;
|
||||
};
|
||||
|
||||
///@endcond
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user