mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -04:00
Merge pull request #8560 from 3nids/locator_context
Add context menu for locator filter results
This commit is contained in:
commit
dde309439c
@ -10,6 +10,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsLocatorResult
|
||||
{
|
||||
%Docstring
|
||||
@ -47,8 +48,31 @@ Constructor for QgsLocatorResult.
|
||||
|
||||
QString group;
|
||||
|
||||
struct ResultAction
|
||||
{
|
||||
public:
|
||||
ResultAction();
|
||||
%Docstring
|
||||
Constructor for ResultAction
|
||||
%End
|
||||
|
||||
ResultAction( int id, QString text, QString iconPath = QString() );
|
||||
%Docstring
|
||||
Constructor for ResultAction
|
||||
The ``id`` used to recognized the action when the result is triggered.
|
||||
It should be 0 or greater as otherwise, the result will be triggered
|
||||
normally.
|
||||
%End
|
||||
int id;
|
||||
QString text;
|
||||
QString iconPath;
|
||||
};
|
||||
|
||||
QList<QgsLocatorResult::ResultAction> actions;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class QgsLocatorFilter : QObject
|
||||
{
|
||||
%Docstring
|
||||
@ -172,6 +196,16 @@ by a user. The filter subclass must implement logic here
|
||||
to perform the desired operation for the search result.
|
||||
E.g. a file search filter would open file associated with the triggered
|
||||
result.
|
||||
%End
|
||||
|
||||
virtual void triggerResultFromAction( const QgsLocatorResult &result, const int actionId );
|
||||
%Docstring
|
||||
Triggers a filter ``result`` from this filter for an entry in the context menu.
|
||||
The entry is identified by its ``actionId`` as specified in the result of this filter.
|
||||
|
||||
.. seealso:: :py:func:`triggerResult`
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
virtual void clearPreviousResults();
|
||||
|
@ -36,6 +36,7 @@ in order to ensure correct sorting of results by priority and match level.
|
||||
ResultScoreRole,
|
||||
ResultFilterNameRole,
|
||||
ResultFilterGroupSortingRole,
|
||||
ResultActionsRole,
|
||||
};
|
||||
|
||||
QgsLocatorModel( QObject *parent /TransferThis/ = 0 );
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsLocatorModelBridge : QObject
|
||||
{
|
||||
%Docstring
|
||||
@ -56,9 +57,9 @@ Returns true if some text to be search is pending in the queue
|
||||
Returns true if the a search is currently running
|
||||
%End
|
||||
|
||||
void triggerResult( const QModelIndex &index );
|
||||
void triggerResult( const QModelIndex &index, const int actionId = -1 );
|
||||
%Docstring
|
||||
Triggers the result at given index
|
||||
Triggers the result at given ``index`` and with optional ``actionId`` if an additional action was triggered
|
||||
%End
|
||||
|
||||
signals:
|
||||
|
@ -1006,7 +1006,8 @@ while ($LINE_IDX < $LINE_COUNT){
|
||||
};
|
||||
|
||||
# remove struct member assignment
|
||||
if ( $SIP_RUN != 1 && $ACCESS[$#ACCESS] == PUBLIC && $LINE =~ m/^(\s*\w+[\w<> *&:,]* \*?\w+) = [\-\w\:\.]+(\([^()]*\))?\s*;/ ){
|
||||
# https://regex101.com/r/tWRGkY/2
|
||||
if ( $SIP_RUN != 1 && $ACCESS[$#ACCESS] == PUBLIC && $LINE =~ m/^(\s*\w+[\w<> *&:,]* \*?\w+) = [\-\w\:\.]+(<\w+( \*)?>)?(\([^()]*\))?\s*;/ ){
|
||||
dbg_info("remove struct member assignment");
|
||||
$LINE = "$1;";
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <QToolButton>
|
||||
#include <QClipboard>
|
||||
|
||||
#include "qgsinbuiltlocatorfilters.h"
|
||||
#include "qgsproject.h"
|
||||
@ -25,8 +27,7 @@
|
||||
#include "qgsmaplayermodel.h"
|
||||
#include "qgslayoutmanager.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include <QToolButton>
|
||||
#include <QClipboard>
|
||||
#include "qgsfeatureaction.h"
|
||||
|
||||
QgsLayerTreeLocatorFilter::QgsLayerTreeLocatorFilter( QObject *parent )
|
||||
: QgsLocatorFilter( parent )
|
||||
@ -400,6 +401,8 @@ void QgsAllLayersFeaturesLocatorFilter::fetchResults( const QString &string, con
|
||||
result.userData = QVariantList() << f.id() << preparedLayer.layerId;
|
||||
result.icon = preparedLayer.layerIcon;
|
||||
result.score = static_cast< double >( string.length() ) / result.displayString.size();
|
||||
|
||||
result.actions << QgsLocatorResult::ResultAction( OpenForm, tr( "Open form…" ) );
|
||||
emit resultFetched( result );
|
||||
|
||||
foundInCurrentLayer++;
|
||||
@ -413,15 +416,41 @@ void QgsAllLayersFeaturesLocatorFilter::fetchResults( const QString &string, con
|
||||
}
|
||||
|
||||
void QgsAllLayersFeaturesLocatorFilter::triggerResult( const QgsLocatorResult &result )
|
||||
{
|
||||
triggerResultFromAction( result, NoEntry );
|
||||
}
|
||||
|
||||
void QgsAllLayersFeaturesLocatorFilter::triggerResultFromAction( const QgsLocatorResult &result, const int actionId )
|
||||
{
|
||||
QVariantList dataList = result.userData.toList();
|
||||
QgsFeatureId id = dataList.at( 0 ).toLongLong();
|
||||
QgsFeatureId fid = dataList.at( 0 ).toLongLong();
|
||||
QString layerId = dataList.at( 1 ).toString();
|
||||
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
|
||||
if ( !layer )
|
||||
return;
|
||||
|
||||
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
|
||||
if ( actionId == OpenForm )
|
||||
{
|
||||
QgsFeature f;
|
||||
QgsFeatureRequest request;
|
||||
request.setFilterFid( fid );
|
||||
bool fetched = layer->getFeatures( request ).nextFeature( f );
|
||||
if ( !fetched )
|
||||
return;
|
||||
QgsFeatureAction action( tr( "Attributes changed" ), f, layer, QString(), -1, QgisApp::instance() );
|
||||
if ( layer->isEditable() )
|
||||
{
|
||||
action.editFeature( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
action.viewFeatureForm();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << fid );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -119,6 +119,12 @@ class APP_EXPORT QgsAllLayersFeaturesLocatorFilter : public QgsLocatorFilter
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ContextMenuEntry
|
||||
{
|
||||
NoEntry,
|
||||
OpenForm
|
||||
};
|
||||
|
||||
struct PreparedLayer
|
||||
{
|
||||
public:
|
||||
@ -128,7 +134,7 @@ class APP_EXPORT QgsAllLayersFeaturesLocatorFilter : public QgsLocatorFilter
|
||||
QString layerName;
|
||||
QString layerId;
|
||||
QIcon layerIcon;
|
||||
} ;
|
||||
};
|
||||
|
||||
QgsAllLayersFeaturesLocatorFilter( QObject *parent = nullptr );
|
||||
QgsAllLayersFeaturesLocatorFilter *clone() const override;
|
||||
@ -140,6 +146,7 @@ class APP_EXPORT QgsAllLayersFeaturesLocatorFilter : public QgsLocatorFilter
|
||||
void prepare( const QString &string, const QgsLocatorContext &context ) override;
|
||||
void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
|
||||
void triggerResult( const QgsLocatorResult &result ) override;
|
||||
void triggerResultFromAction( const QgsLocatorResult &result, const int actionId ) override;
|
||||
|
||||
private:
|
||||
int mMaxResultsPerLayer = 6;
|
||||
|
@ -33,6 +33,12 @@ QgsLocatorFilter::Flags QgsLocatorFilter::flags() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QgsLocatorFilter::triggerResultFromAction( const QgsLocatorResult &result, const int actionId )
|
||||
{
|
||||
Q_UNUSED( result );
|
||||
Q_UNUSED( actionId );
|
||||
}
|
||||
|
||||
bool QgsLocatorFilter::stringMatches( const QString &candidate, const QString &search )
|
||||
{
|
||||
return !search.isEmpty() && candidate.contains( search, Qt::CaseInsensitive );
|
||||
|
@ -18,12 +18,14 @@
|
||||
#ifndef QGSLOCATORFILTER_H
|
||||
#define QGSLOCATORFILTER_H
|
||||
|
||||
#include <QAction>
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgslocatorcontext.h"
|
||||
#include "qgslogger.h"
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QIcon>
|
||||
|
||||
class QgsFeedback;
|
||||
class QgsLocatorFilter;
|
||||
@ -87,12 +89,50 @@ class CORE_EXPORT QgsLocatorResult
|
||||
* If left as empty string, this means that results are all shown without being grouped.
|
||||
* If a group is given, the results will be grouped by \a group under a header.
|
||||
* \note This should be translated.
|
||||
* \since 3.2
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
QString group = QString();
|
||||
|
||||
/**
|
||||
* The ResultAction stores basic information for additional
|
||||
* actions to be used in a locator widget for the result.
|
||||
* They could be used in a context menu for instance.
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
struct CORE_EXPORT ResultAction
|
||||
{
|
||||
public:
|
||||
//! Constructor for ResultAction
|
||||
ResultAction() = default;
|
||||
|
||||
/**
|
||||
* Constructor for ResultAction
|
||||
* The \a id used to recognized the action when the result is triggered.
|
||||
* It should be 0 or greater as otherwise, the result will be triggered
|
||||
* normally.
|
||||
*/
|
||||
ResultAction( int id, QString text, QString iconPath = QString() )
|
||||
: id( id )
|
||||
, text( text )
|
||||
, iconPath( iconPath )
|
||||
{}
|
||||
int id = -1;
|
||||
QString text;
|
||||
QString iconPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Additional actions to be used in a locator widget
|
||||
* for the given result. They could be displayed in
|
||||
* a context menu.
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
QList<QgsLocatorResult::ResultAction> actions;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE( QgsLocatorResult::ResultAction )
|
||||
|
||||
|
||||
/**
|
||||
* \class QgsLocatorFilter
|
||||
* \ingroup core
|
||||
@ -209,6 +249,14 @@ class CORE_EXPORT QgsLocatorFilter : public QObject
|
||||
*/
|
||||
virtual void triggerResult( const QgsLocatorResult &result ) = 0;
|
||||
|
||||
/**
|
||||
* Triggers a filter \a result from this filter for an entry in the context menu.
|
||||
* The entry is identified by its \a actionId as specified in the result of this filter.
|
||||
* \see triggerResult()
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
virtual void triggerResultFromAction( const QgsLocatorResult &result, const int actionId );
|
||||
|
||||
/**
|
||||
* This method will be called on main thread on the original filter (not a clone)
|
||||
* before fetching results or before triggering a result to clear any change made
|
||||
|
@ -160,6 +160,9 @@ QVariant QgsLocatorModel::data( const QModelIndex &index, int role ) const
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
case ResultActionsRole:
|
||||
return QVariant::fromValue( mResults.at( index.row() ).result.actions );
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@ -188,6 +191,7 @@ QHash<int, QByteArray> QgsLocatorModel::roleNames() const
|
||||
roles[ResultScoreRole] = "ResultScore";
|
||||
roles[ResultFilterNameRole] = "ResultFilterName";
|
||||
roles[ResultFilterGroupSortingRole] = "ResultFilterGroupSorting";
|
||||
roles[ResultActionsRole] = "ResultContextMenuActions";
|
||||
roles[Qt::DisplayRole] = "Text";
|
||||
return roles;
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ class CORE_EXPORT QgsLocatorModel : public QAbstractTableModel
|
||||
ResultScoreRole, //!< Result match score, used by QgsLocatorProxyModel for sorting roles.
|
||||
ResultFilterNameRole, //!< Associated filter name which created the result
|
||||
ResultFilterGroupSortingRole, //!< Group results within the same filter results
|
||||
ResultActionsRole, //!< The actions to be shown for the given result in a context menu
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -37,12 +37,17 @@ bool QgsLocatorModelBridge::isRunning() const
|
||||
return mIsRunning;
|
||||
}
|
||||
|
||||
void QgsLocatorModelBridge::triggerResult( const QModelIndex &index )
|
||||
void QgsLocatorModelBridge::triggerResult( const QModelIndex &index, const int actionId )
|
||||
{
|
||||
mLocator->clearPreviousResults();
|
||||
QgsLocatorResult result = mProxyModel->data( index, QgsLocatorModel::ResultDataRole ).value< QgsLocatorResult >();
|
||||
if ( result.filter )
|
||||
result.filter->triggerResult( result );
|
||||
{
|
||||
if ( actionId >= 0 )
|
||||
result.filter->triggerResultFromAction( result, actionId );
|
||||
else
|
||||
result.filter->triggerResult( result );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLocatorModelBridge::setIsRunning( bool isRunning )
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
#include "qgsrectangle.h"
|
||||
|
||||
class QAction;
|
||||
|
||||
class QgsLocatorResult;
|
||||
class QgsLocator;
|
||||
class QgsLocatorContext;
|
||||
@ -61,8 +63,8 @@ class CORE_EXPORT QgsLocatorModelBridge : public QObject
|
||||
//! Returns true if the a search is currently running
|
||||
bool isRunning() const;
|
||||
|
||||
//! Triggers the result at given index
|
||||
void triggerResult( const QModelIndex &index );
|
||||
//! Triggers the result at given \a index and with optional \a actionId if an additional action was triggered
|
||||
void triggerResult( const QModelIndex &index, const int actionId = -1 );
|
||||
|
||||
signals:
|
||||
//! Emitted when a result is added
|
||||
|
@ -74,9 +74,12 @@ QgsLocatorWidget::QgsLocatorWidget( QWidget *parent )
|
||||
mResultsView->setUniformRowHeights( true );
|
||||
mResultsView->setIconSize( QSize( 16, 16 ) );
|
||||
mResultsView->recalculateSize();
|
||||
mResultsView->setContextMenuPolicy( Qt::CustomContextMenu );
|
||||
|
||||
connect( mLineEdit, &QLineEdit::textChanged, this, &QgsLocatorWidget::scheduleDelayedPopup );
|
||||
connect( mResultsView, &QAbstractItemView::activated, this, &QgsLocatorWidget::acceptCurrentEntry );
|
||||
connect( mResultsView, &QAbstractItemView::customContextMenuRequested, this, &QgsLocatorWidget::showContextMenu );
|
||||
|
||||
connect( mModelBridge, &QgsLocatorModelBridge::resultAdded, this, &QgsLocatorWidget::resultAdded );
|
||||
connect( mModelBridge, &QgsLocatorModelBridge::isRunningChanged, this, [ = ]() {mLineEdit->setShowSpinner( mModelBridge->isRunning() );} );
|
||||
connect( mModelBridge, & QgsLocatorModelBridge::resultsCleared, this, [ = ]() {mHasSelectedResult = false;} );
|
||||
@ -172,6 +175,25 @@ void QgsLocatorWidget::resultAdded()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLocatorWidget::showContextMenu( const QPoint &point )
|
||||
{
|
||||
QModelIndex index = mResultsView->indexAt( point );
|
||||
if ( !index.isValid() )
|
||||
return;
|
||||
|
||||
const QList<QgsLocatorResult::ResultAction> actions = mResultsView->model()->data( index, QgsLocatorModel::ResultActionsRole ).value<QList<QgsLocatorResult::ResultAction>>();
|
||||
QMenu *contextMenu = new QMenu( mResultsView );
|
||||
for ( auto resultAction : actions )
|
||||
{
|
||||
QAction *menuAction = new QAction( resultAction.text, contextMenu );
|
||||
if ( !resultAction.iconPath.isEmpty() )
|
||||
menuAction->setIcon( QIcon( resultAction.iconPath ) );
|
||||
connect( menuAction, &QAction::triggered, this, [ = ]() {mModelBridge->triggerResult( index, resultAction.id );} );
|
||||
contextMenu->addAction( menuAction );
|
||||
}
|
||||
contextMenu->exec( mResultsView->viewport()->mapToGlobal( point ) );
|
||||
}
|
||||
|
||||
void QgsLocatorWidget::performSearch()
|
||||
{
|
||||
mPopupTimer.stop();
|
||||
|
@ -94,6 +94,7 @@ class GUI_EXPORT QgsLocatorWidget : public QWidget
|
||||
void configMenuAboutToShow();
|
||||
void scheduleDelayedPopup();
|
||||
void resultAdded();
|
||||
void showContextMenu( const QPoint &point );
|
||||
|
||||
private:
|
||||
QgsLocatorModelBridge *mModelBridge = nullptr;
|
||||
|
@ -93,6 +93,7 @@ typedef QtClass<QVariant> QtClassQVariantBase;
|
||||
QString mName;
|
||||
int mCount;
|
||||
QgsMapLayer *mLayer;
|
||||
QList<QAction *> contextMenuActions;
|
||||
};
|
||||
|
||||
static const int MONTHS;
|
||||
|
@ -134,6 +134,7 @@ class CORE_EXPORT QgsSipifyHeader : public QtClass<QVariant>, private Ui::QgsBas
|
||||
QString mName;
|
||||
int mCount = 100;
|
||||
QgsMapLayer *mLayer = nullptr;
|
||||
QList<QAction *> contextMenuActions = QList<QAction *>();
|
||||
};
|
||||
|
||||
static const int MONTHS = 60 * 60 * 24 * 30; // something
|
||||
|
Loading…
x
Reference in New Issue
Block a user