35 , mResultsView( new QgsLocatorResultsView() )
39 mLineEdit->setPlaceholderText( tr(
"Type to locate (⌘K)" ) );
41 mLineEdit->setPlaceholderText( tr(
"Type to locate (Ctrl+K)" ) );
44 int placeholderMinWidth = mLineEdit->fontMetrics().width( mLineEdit->placeholderText() );
45 int minWidth = std::max( 200, (
int )( placeholderMinWidth * 1.6 ) );
46 resize( minWidth, 30 );
47 QSizePolicy sizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
48 sizePolicy.setHorizontalStretch( 0 );
49 sizePolicy.setVerticalStretch( 0 );
50 setSizePolicy( sizePolicy );
51 setMinimumSize( QSize( minWidth, 0 ) );
53 QHBoxLayout *layout =
new QHBoxLayout();
54 layout->setMargin( 0 );
55 layout->setContentsMargins( 0, 0, 0, 0 );
56 layout->addWidget( mLineEdit );
59 setFocusProxy( mLineEdit );
67 QHBoxLayout *containerLayout =
new QHBoxLayout();
68 containerLayout->setMargin( 0 );
69 containerLayout->setContentsMargins( 0, 0, 0, 0 );
70 containerLayout->addWidget( mResultsView );
71 mResultsContainer->setLayout( containerLayout );
72 mResultsContainer->hide();
75 mProxyModel->setSourceModel( mLocatorModel );
76 mResultsView->setModel( mProxyModel );
77 mResultsView->setUniformRowHeights(
true );
78 mResultsView->setIconSize( QSize( 16, 16 ) );
79 mResultsView->recalculateSize();
83 connect( mLineEdit, &QLineEdit::textChanged,
this, &QgsLocatorWidget::scheduleDelayedPopup );
84 connect( mResultsView, &QAbstractItemView::activated,
this, &QgsLocatorWidget::acceptCurrentEntry );
87 mPopupTimer.setInterval( 100 );
88 mPopupTimer.setSingleShot(
true );
89 connect( &mPopupTimer, &QTimer::timeout,
this, &QgsLocatorWidget::performSearch );
90 mFocusTimer.setInterval( 110 );
91 mFocusTimer.setSingleShot(
true );
92 connect( &mFocusTimer, &QTimer::timeout,
this, &QgsLocatorWidget::triggerSearchAndShowList );
94 mLineEdit->installEventFilter(
this );
95 mResultsContainer->installEventFilter(
this );
96 mResultsView->installEventFilter(
this );
97 installEventFilter(
this );
98 window()->installEventFilter(
this );
100 mLocator->
registerFilter(
new QgsLocatorFilterFilter(
this,
this ) );
102 mMenu =
new QMenu(
this );
103 QAction *menuAction = mLineEdit->addAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/search.svg" ) ), QLineEdit::LeadingPosition );
104 connect( menuAction, &QAction::triggered,
this, [ = ]
107 mResultsContainer->hide();
108 mMenu->exec( QCursor::pos() );
110 connect( mMenu, &QMenu::aboutToShow,
this, &QgsLocatorWidget::configMenuAboutToShow );
126 mLineEdit->setText(
string );
127 window()->activateWindow();
128 mLineEdit->setFocus();
135 mLocatorModel->
clear();
136 mResultsContainer->hide();
139 void QgsLocatorWidget::scheduleDelayedPopup()
144 void QgsLocatorWidget::performSearch()
147 updateResults( mLineEdit->text() );
151 void QgsLocatorWidget::showList()
153 mResultsContainer->show();
154 mResultsContainer->raise();
157 void QgsLocatorWidget::triggerSearchAndShowList()
159 if ( mProxyModel->rowCount() == 0 )
165 void QgsLocatorWidget::searchFinished()
167 if ( mHasQueuedRequest )
170 QString nextSearch = mNextRequestedString;
171 mNextRequestedString.clear();
172 mHasQueuedRequest =
false;
173 updateResults( nextSearch );
184 if ( obj == mLineEdit && event->type() == QEvent::KeyPress )
186 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>( event );
187 switch ( keyEvent->key() )
192 case Qt::Key_PageDown:
193 triggerSearchAndShowList();
194 mHasSelectedResult =
true;
195 QgsApplication::sendEvent( mResultsView, event );
199 if ( keyEvent->modifiers() & Qt::ControlModifier )
201 triggerSearchAndShowList();
202 mHasSelectedResult =
true;
203 QgsApplication::sendEvent( mResultsView, event );
209 acceptCurrentEntry();
212 mResultsContainer->hide();
215 mHasSelectedResult =
true;
216 mResultsView->selectNextResult();
218 case Qt::Key_Backtab:
219 mHasSelectedResult =
true;
220 mResultsView->selectPreviousResult();
226 else if ( obj == mResultsView && event->type() == QEvent::MouseButtonPress )
228 mHasSelectedResult =
true;
230 else if ( event->type() == QEvent::FocusOut && ( obj == mLineEdit || obj == mResultsContainer || obj == mResultsView ) )
232 if ( !mLineEdit->hasFocus() && !mResultsContainer->hasFocus() && !mResultsView->hasFocus() )
235 mResultsContainer->hide();
238 else if ( event->type() == QEvent::FocusIn && obj == mLineEdit )
242 else if ( obj == window() && event->type() == QEvent::Resize )
244 mResultsView->recalculateSize();
246 return QWidget::eventFilter( obj, event );
251 bool selectFirst = !mHasSelectedResult || mProxyModel->rowCount() == 0;
255 int row = mProxyModel->flags( mProxyModel->index( 0, 0 ) ) & Qt::ItemIsSelectable ? 0 : 1;
256 mResultsView->setCurrentIndex( mProxyModel->index( row, 0 ) );
260 void QgsLocatorWidget::configMenuAboutToShow()
263 QMap< QString, QgsLocatorFilter *> filters = mLocator->
prefixedFilters();
264 QMap< QString, QgsLocatorFilter *>::const_iterator fIt = filters.constBegin();
265 for ( ; fIt != filters.constEnd(); ++fIt )
267 if ( !fIt.value()->enabled() )
270 QAction *action =
new QAction( fIt.value()->displayName(), mMenu );
271 connect( action, &QAction::triggered,
this, [ = ]
273 QString currentText = mLineEdit->text();
274 if ( currentText.isEmpty() )
275 currentText = tr(
"<type here>" );
278 QStringList parts = currentText.split(
' ' );
279 if ( parts.count() > 1 && mLocator->
prefixedFilters().contains( parts.at( 0 ) ) )
282 currentText = parts.join(
' ' );
286 mLineEdit->setText( fIt.key() +
' ' + currentText );
287 mLineEdit->setSelection( fIt.key().length() + 1, currentText.length() );
289 mMenu->addAction( action );
291 mMenu->addSeparator();
292 QAction *configAction =
new QAction( tr(
"Configure…" ), mMenu );
294 mMenu->addAction( configAction );
298 void QgsLocatorWidget::updateResults(
const QString &text )
307 mNextRequestedString = text;
308 mHasQueuedRequest =
true;
313 mHasSelectedResult =
false;
319 void QgsLocatorWidget::acceptCurrentEntry()
321 if ( mHasQueuedRequest )
327 if ( !mResultsView->isVisible() )
330 QModelIndex index = mResultsView->currentIndex();
331 if ( !index.isValid() )
335 mResultsContainer->hide();
336 mLineEdit->clearFocus();
358 QgsLocatorResultsView::QgsLocatorResultsView( QWidget *parent )
359 : QTreeView( parent )
361 setRootIsDecorated(
false );
362 setUniformRowHeights(
true );
364 header()->setStretchLastSection(
true );
367 void QgsLocatorResultsView::recalculateSize()
370 int rowSize = 20 * itemDelegate()->sizeHint( viewOptions(), model()->index( 0, 0 ) ).height();
373 int width = std::max( 300, window()->size().width() / 2 );
374 QSize newSize( width, rowSize + frameWidth() * 2 );
376 parentWidget()->resize( newSize );
377 QTreeView::resize( newSize );
379 header()->resizeSection( 0, width / 2 );
380 header()->resizeSection( 1, 0 );
383 void QgsLocatorResultsView::selectNextResult()
385 int nextRow = currentIndex().row() + 1;
386 nextRow = nextRow % model()->rowCount( QModelIndex() );
387 setCurrentIndex( model()->index( nextRow, 0 ) );
390 void QgsLocatorResultsView::selectPreviousResult()
392 int previousRow = currentIndex().row() - 1;
393 if ( previousRow < 0 )
394 previousRow = model()->rowCount( QModelIndex() ) - 1;
395 setCurrentIndex( model()->index( previousRow, 0 ) );
404 , mLocator( locator )
407 QgsLocatorFilterFilter *QgsLocatorFilterFilter::clone()
const 409 return new QgsLocatorFilterFilter( mLocator );
412 QgsLocatorFilter::Flags QgsLocatorFilterFilter::flags()
const 419 if ( !
string.isEmpty() )
425 QMap< QString, QgsLocatorFilter *> filters = mLocator->locator()->
prefixedFilters();
426 QMap< QString, QgsLocatorFilter *>::const_iterator fIt = filters.constBegin();
427 for ( ; fIt != filters.constEnd(); ++fIt )
432 if ( fIt.value() ==
this || !fIt.value() || !fIt.value()->enabled() )
440 emit resultFetched( result );
444 void QgsLocatorFilterFilter::triggerResult(
const QgsLocatorResult &result )
446 mLocator->search( result.
userData.toString() );
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
void cancelWithoutBlocking()
Triggers cancelation of any current running query without blocking.
QIcon icon
Icon for result.
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.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
bool isRunning() const
Returns true if a query is currently being executed by the locator.
QString description
Descriptive text for result.
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
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 cancelation of something running in a worker thread...
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 ...
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
QgsLocatorFilter * filter
Filter from which the result was obtained.
bool isCanceled() const
Tells whether the operation has been canceled already.
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.
QMap< QString, QgsLocatorFilter * > prefixedFilters() const
Returns a map of prefix to filter, for all registered filters with valid prefixes.
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
void clear()
Resets the model and clears all existing results.