36 , mResultsView( new QgsLocatorResultsView() )
40 mLineEdit->setPlaceholderText( tr(
"Type to locate (⌘K)" ) );
42 mLineEdit->setPlaceholderText( tr(
"Type to locate (Ctrl+K)" ) );
45 int placeholderMinWidth = mLineEdit->fontMetrics().width( mLineEdit->placeholderText() );
46 int minWidth = std::max( 200, static_cast< int >( placeholderMinWidth * 1.8 ) );
47 resize( minWidth, 30 );
48 QSizePolicy sizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
49 sizePolicy.setHorizontalStretch( 0 );
50 sizePolicy.setVerticalStretch( 0 );
51 setSizePolicy( sizePolicy );
52 setMinimumSize( QSize( minWidth, 0 ) );
54 QHBoxLayout *layout =
new QHBoxLayout();
55 layout->setMargin( 0 );
56 layout->setContentsMargins( 0, 0, 0, 0 );
57 layout->addWidget( mLineEdit );
60 setFocusProxy( mLineEdit );
68 QHBoxLayout *containerLayout =
new QHBoxLayout();
69 containerLayout->setMargin( 0 );
70 containerLayout->setContentsMargins( 0, 0, 0, 0 );
71 containerLayout->addWidget( mResultsView );
72 mResultsContainer->setLayout( containerLayout );
73 mResultsContainer->hide();
76 mProxyModel->setSourceModel( mLocatorModel );
77 mResultsView->setModel( mProxyModel );
78 mResultsView->setUniformRowHeights(
true );
81 mResultsView->setIconSize( QSize( iconSize, iconSize ) );
82 mResultsView->recalculateSize();
86 connect( mLineEdit, &QLineEdit::textChanged,
this, &QgsLocatorWidget::scheduleDelayedPopup );
87 connect( mResultsView, &QAbstractItemView::activated,
this, &QgsLocatorWidget::acceptCurrentEntry );
90 mPopupTimer.setInterval( 100 );
91 mPopupTimer.setSingleShot(
true );
92 connect( &mPopupTimer, &QTimer::timeout,
this, &QgsLocatorWidget::performSearch );
93 mFocusTimer.setInterval( 110 );
94 mFocusTimer.setSingleShot(
true );
95 connect( &mFocusTimer, &QTimer::timeout,
this, &QgsLocatorWidget::triggerSearchAndShowList );
97 mLineEdit->installEventFilter(
this );
98 mResultsContainer->installEventFilter(
this );
99 mResultsView->installEventFilter(
this );
100 installEventFilter(
this );
101 window()->installEventFilter(
this );
103 mLocator->
registerFilter(
new QgsLocatorFilterFilter(
this,
this ) );
105 mMenu =
new QMenu(
this );
106 QAction *menuAction = mLineEdit->addAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/search.svg" ) ), QLineEdit::LeadingPosition );
107 connect( menuAction, &QAction::triggered,
this, [ = ]
110 mResultsContainer->hide();
111 mMenu->exec( QCursor::pos() );
113 connect( mMenu, &QMenu::aboutToShow,
this, &QgsLocatorWidget::configMenuAboutToShow );
129 mLineEdit->setText(
string );
130 window()->activateWindow();
131 mLineEdit->setFocus();
138 mLocatorModel->
clear();
139 mResultsContainer->hide();
142 void QgsLocatorWidget::scheduleDelayedPopup()
147 void QgsLocatorWidget::performSearch()
150 updateResults( mLineEdit->text() );
154 void QgsLocatorWidget::showList()
156 mResultsContainer->show();
157 mResultsContainer->raise();
160 void QgsLocatorWidget::triggerSearchAndShowList()
162 if ( mProxyModel->rowCount() == 0 )
168 void QgsLocatorWidget::searchFinished()
170 if ( mHasQueuedRequest )
173 QString nextSearch = mNextRequestedString;
174 mNextRequestedString.clear();
175 mHasQueuedRequest =
false;
176 updateResults( nextSearch );
187 if ( obj == mLineEdit && event->type() == QEvent::KeyPress )
189 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>( event );
190 switch ( keyEvent->key() )
195 case Qt::Key_PageDown:
196 triggerSearchAndShowList();
197 mHasSelectedResult =
true;
198 QgsApplication::sendEvent( mResultsView, event );
202 if ( keyEvent->modifiers() & Qt::ControlModifier )
204 triggerSearchAndShowList();
205 mHasSelectedResult =
true;
206 QgsApplication::sendEvent( mResultsView, event );
212 acceptCurrentEntry();
215 mResultsContainer->hide();
218 mHasSelectedResult =
true;
219 mResultsView->selectNextResult();
221 case Qt::Key_Backtab:
222 mHasSelectedResult =
true;
223 mResultsView->selectPreviousResult();
229 else if ( obj == mResultsView && event->type() == QEvent::MouseButtonPress )
231 mHasSelectedResult =
true;
233 else if ( event->type() == QEvent::FocusOut && ( obj == mLineEdit || obj == mResultsContainer || obj == mResultsView ) )
235 if ( !mLineEdit->hasFocus() && !mResultsContainer->hasFocus() && !mResultsView->hasFocus() )
238 mResultsContainer->hide();
241 else if ( event->type() == QEvent::FocusIn && obj == mLineEdit )
245 else if ( obj == window() && event->type() == QEvent::Resize )
247 mResultsView->recalculateSize();
249 return QWidget::eventFilter( obj, event );
254 bool selectFirst = !mHasSelectedResult || mProxyModel->rowCount() == 0;
259 bool selectable =
false;
260 while ( !selectable && row < mProxyModel->rowCount() )
263 selectable = mProxyModel->flags( mProxyModel->index( row, 0 ) ).testFlag( Qt::ItemIsSelectable );
266 mResultsView->setCurrentIndex( mProxyModel->index( row, 0 ) );
270 void QgsLocatorWidget::configMenuAboutToShow()
275 if ( !filter->enabled() )
278 QAction *action =
new QAction( filter->displayName(), mMenu );
279 connect( action, &QAction::triggered,
this, [ = ]
281 QString currentText = mLineEdit->text();
282 if ( currentText.isEmpty() )
283 currentText = tr(
"<type here>" );
286 QStringList parts = currentText.split(
' ' );
287 if ( parts.count() > 1 && mLocator->
filters( parts.at( 0 ) ).count() > 0 )
290 currentText = parts.join(
' ' );
294 mLineEdit->setText( filter->activePrefix() +
' ' + currentText );
295 mLineEdit->setSelection( filter->activePrefix().length() + 1, currentText.length() );
297 mMenu->addAction( action );
299 mMenu->addSeparator();
300 QAction *configAction =
new QAction( tr(
"Configure…" ), mMenu );
302 mMenu->addAction( configAction );
306 void QgsLocatorWidget::updateResults(
const QString &text )
315 mNextRequestedString = text;
316 mHasQueuedRequest =
true;
321 mHasSelectedResult =
false;
327 void QgsLocatorWidget::acceptCurrentEntry()
329 if ( mHasQueuedRequest )
335 if ( !mResultsView->isVisible() )
338 QModelIndex index = mResultsView->currentIndex();
339 if ( !index.isValid() )
343 mResultsContainer->hide();
344 mLineEdit->clearFocus();
367 QgsLocatorResultsView::QgsLocatorResultsView( QWidget *parent )
368 : QTreeView( parent )
370 setRootIsDecorated(
false );
371 setUniformRowHeights(
true );
373 header()->setStretchLastSection(
true );
376 void QgsLocatorResultsView::recalculateSize()
379 int rowSize = 20 * itemDelegate()->sizeHint( viewOptions(), model()->index( 0, 0 ) ).height();
382 int width = std::max( 300, window()->size().width() / 2 );
383 QSize newSize( width, rowSize + frameWidth() * 2 );
385 parentWidget()->resize( newSize );
386 QTreeView::resize( newSize );
388 header()->resizeSection( 0, width / 2 );
389 header()->resizeSection( 1, 0 );
392 void QgsLocatorResultsView::selectNextResult()
394 int nextRow = currentIndex().row() + 1;
395 nextRow = nextRow % model()->rowCount( QModelIndex() );
396 setCurrentIndex( model()->index( nextRow, 0 ) );
399 void QgsLocatorResultsView::selectPreviousResult()
401 int previousRow = currentIndex().row() - 1;
402 if ( previousRow < 0 )
403 previousRow = model()->rowCount( QModelIndex() ) - 1;
404 setCurrentIndex( model()->index( previousRow, 0 ) );
413 , mLocator( locator )
416 QgsLocatorFilterFilter *QgsLocatorFilterFilter::clone()
const 418 return new QgsLocatorFilterFilter( mLocator );
421 QgsLocatorFilter::Flags QgsLocatorFilterFilter::flags()
const 428 if ( !
string.isEmpty() )
439 if ( filter ==
this || !filter || !filter->enabled() )
445 result.
userData = filter->activePrefix() +
' ';
447 emit resultFetched( result );
451 void QgsLocatorFilterFilter::triggerResult(
const QgsLocatorResult &result )
453 mLocator->search( result.
userData.toString() );
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
bool isCanceled() const
Tells whether the operation has been canceled already.
void cancelWithoutBlocking()
Triggers cancellation of any current running query without blocking.
QIcon icon
Icon for result.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
void fetchResults(const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback=nullptr)
Triggers the background fetching of filter results for a specified search string. ...
A sort proxy model for QgsLocatorModel, which automatically sorts results by precedence.
void setShowSpinner(bool showSpinner)
Show a spinner icon.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
QString description
Descriptive text for result.
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Map canvas is a class for displaying all GIS data types on a canvas.
void finished()
Emitted when locator has finished a query, either as a result of successful completion or early cance...
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
Base class for feedback objects to be used for cancellation of something running in a worker thread...
bool isRunning() const
Returns true if a query is currently being executed by the locator.
QgsRectangle targetExtent
Map extent to target in results.
virtual void triggerResult(const QgsLocatorResult &result)=0
Triggers a filter result from this filter.
QVariant userData
Custom reference or other data set by the filter.
QString displayString
String displayed for result.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
Encapsulates the properties relating to the context of a locator search.
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
Abstract base class for filters which collect locator results.
Handles the management of QgsLocatorFilter objects and async collection of search results from them...
void foundResult(const QgsLocatorResult &result)
Emitted whenever a filter encounters a matching result after the fetchResults() method is called...
An abstract list model for displaying the results of locator searches.
QgsCoordinateReferenceSystem targetExtentCrs
Coordinate reference system for the map extent variable.
void deferredClear()
Resets the model and clears all existing results after a short delay, or whenever the next result is ...
QgsLocatorFilter * filter
Filter from which the result was obtained.
void addResult(const QgsLocatorResult &result)
Adds a new result to the model.
Filter finds results quickly and can be safely run in the main thread.
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
void clear()
Resets the model and clears all existing results.
void clearPreviousResults()
Will call clearPreviousResults on all filters.