From c1e16969f33ca63e544bb892da5b45afcc2d067e Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Thu, 17 May 2018 13:11:10 -0400 Subject: [PATCH] [QgsLocator] add the capability of adding group for elements within the same filter --- .../locator/qgslocatorfilter.sip.in | 2 + .../locator/qgslocatormodel.sip.in | 3 + scripts/sipify.pl | 4 +- src/core/locator/qgslocatorfilter.h | 9 +++ src/core/locator/qgslocatormodel.cpp | 70 ++++++++++++++++--- src/core/locator/qgslocatormodel.h | 6 ++ src/gui/locator/qgslocatorwidget.cpp | 11 ++- 7 files changed, 93 insertions(+), 12 deletions(-) diff --git a/python/core/auto_generated/locator/qgslocatorfilter.sip.in b/python/core/auto_generated/locator/qgslocatorfilter.sip.in index 2f5295a4f2c..37694e0d072 100644 --- a/python/core/auto_generated/locator/qgslocatorfilter.sip.in +++ b/python/core/auto_generated/locator/qgslocatorfilter.sip.in @@ -45,6 +45,8 @@ Constructor for QgsLocatorResult. double score; + QString group; + }; class QgsLocatorFilter : QObject diff --git a/python/core/auto_generated/locator/qgslocatormodel.sip.in b/python/core/auto_generated/locator/qgslocatormodel.sip.in index d22cc9ab43a..79bf57cf049 100644 --- a/python/core/auto_generated/locator/qgslocatormodel.sip.in +++ b/python/core/auto_generated/locator/qgslocatormodel.sip.in @@ -26,6 +26,8 @@ in order to ensure correct sorting of results by priority and match level. %End public: + static const int NoGroup; + enum Role { ResultDataRole, @@ -33,6 +35,7 @@ in order to ensure correct sorting of results by priority and match level. ResultFilterPriorityRole, ResultScoreRole, ResultFilterNameRole, + ResultFilterGroupSortingRole, }; QgsLocatorModel( QObject *parent /TransferThis/ = 0 ); diff --git a/scripts/sipify.pl b/scripts/sipify.pl index 5bd928b06f2..91080f0eae7 100755 --- a/scripts/sipify.pl +++ b/scripts/sipify.pl @@ -409,9 +409,9 @@ sub detect_comment_block{ } # Detect if line is a non method member declaration -# https://regex101.com/r/gUBZUk/13 +# https://regex101.com/r/gUBZUk/14 sub detect_non_method_member{ - return 1 if $LINE =~ m/^\s*(?:template\s*<\w+>\s+)?(?:(const|mutable|static|friend|unsigned)\s+)*\w+(::\w+)?(<([\w<> *&,()]|::)+>)?(,?\s+\*?\w+( = (-?\d+(\.\d+)?|((QMap|QList)<[^()]+>\(\))|(\w+::)*\w+(\([^()]+\))?)|\[\d+\])?)+;/; + return 1 if $LINE =~ m/^\s*(?:template\s*<\w+>\s+)?(?:(const|mutable|static|friend|unsigned)\s+)*\w+(::\w+)?(<([\w<> *&,()]|::)+>)?(,?\s+\*?\w+( = (-?\d+(\.\d+)?|((QMap|QList)<[^()]+>\(\))|(\w+::)*\w+(\([^()]?\))?)|\[\d+\])?)+;/; return 0; } diff --git a/src/core/locator/qgslocatorfilter.h b/src/core/locator/qgslocatorfilter.h index 89b2d2ebc14..245319ced4d 100644 --- a/src/core/locator/qgslocatorfilter.h +++ b/src/core/locator/qgslocatorfilter.h @@ -82,6 +82,15 @@ class CORE_EXPORT QgsLocatorResult */ double score = 0.5; + /** + * Group the results by categories + * 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 + */ + QString group = QString(); + }; /** diff --git a/src/core/locator/qgslocatormodel.cpp b/src/core/locator/qgslocatormodel.cpp index aee581530c8..199606e1e81 100644 --- a/src/core/locator/qgslocatormodel.cpp +++ b/src/core/locator/qgslocatormodel.cpp @@ -15,12 +15,14 @@ * * ***************************************************************************/ +#include #include "qgslocatormodel.h" #include "qgslocator.h" #include "qgsapplication.h" #include "qgslogger.h" + // // QgsLocatorModel // @@ -41,6 +43,7 @@ void QgsLocatorModel::clear() beginResetModel(); mResults.clear(); mFoundResultsFromFilterNames.clear(); + mFoundResultsFilterGroups.clear(); endResetModel(); } @@ -76,8 +79,14 @@ QVariant QgsLocatorModel::data( const QModelIndex &index, int role ) const case Name: if ( !mResults.at( index.row() ).filter ) return mResults.at( index.row() ).result.displayString; - else + else if ( mResults.at( index.row() ).filter && mResults.at( index.row() ).groupSorting == 0 ) return mResults.at( index.row() ).filterTitle; + else + { + QString groupTitle = mResults.at( index.row() ).groupTitle; + groupTitle.prepend( " " ); + return groupTitle; + } case Description: if ( !mResults.at( index.row() ).filter ) return mResults.at( index.row() ).result.description; @@ -87,6 +96,19 @@ QVariant QgsLocatorModel::data( const QModelIndex &index, int role ) const break; } + case Qt::FontRole: + if ( index.column() == Name && !mResults.at( index.row() ).groupTitle.isEmpty() ) + { + QFont font; + font.setItalic( true ); + return font; + } + else + { + return QVariant(); + } + break; + case Qt::DecorationRole: switch ( index.column() ) { @@ -112,10 +134,8 @@ QVariant QgsLocatorModel::data( const QModelIndex &index, int role ) const return QVariant(); case ResultTypeRole: - if ( mResults.at( index.row() ).filter ) - return 0; - else - return 1; + // 0 for filter title, the group otherwise, 9999 if no group + return mResults.at( index.row() ).groupSorting; case ResultScoreRole: if ( mResults.at( index.row() ).filter ) @@ -134,6 +154,12 @@ QVariant QgsLocatorModel::data( const QModelIndex &index, int role ) const return mResults.at( index.row() ).result.filter->displayName(); else return mResults.at( index.row() ).filterTitle; + + case ResultFilterGroupSortingRole: + if ( mResults.at( index.row() ).groupTitle.isEmpty() ) + return 1; + else + return 0; } return QVariant(); @@ -146,7 +172,7 @@ Qt::ItemFlags QgsLocatorModel::flags( const QModelIndex &index ) const return QAbstractTableModel::flags( index ); Qt::ItemFlags flags = QAbstractTableModel::flags( index ); - if ( !mResults.at( index.row() ).filterTitle.isEmpty() ) + if ( mResults.at( index.row() ).filter ) { flags = flags & ~( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); } @@ -159,6 +185,7 @@ void QgsLocatorModel::addResult( const QgsLocatorResult &result ) if ( mDeferredClear ) { mFoundResultsFromFilterNames.clear(); + mFoundResultsFilterGroups.clear(); } int pos = mResults.size(); @@ -166,13 +193,21 @@ void QgsLocatorModel::addResult( const QgsLocatorResult &result ) if ( addingFilter ) mFoundResultsFromFilterNames << result.filter->name(); + bool addingGroup = !result.group.isEmpty() && ( !mFoundResultsFilterGroups.contains( result.filter ) + || !mFoundResultsFilterGroups.value( result.filter ).contains( result.group ) ); + if ( addingGroup ) + { + if ( !mFoundResultsFilterGroups.contains( result.filter ) ) + mFoundResultsFilterGroups[result.filter] = QStringList(); + mFoundResultsFilterGroups[result.filter] << result.group ; + } if ( mDeferredClear ) { beginResetModel(); mResults.clear(); } else - beginInsertRows( QModelIndex(), pos, pos + ( addingFilter ? 1 : 0 ) ); + beginInsertRows( QModelIndex(), pos, pos + ( static_cast( addingFilter ) + static_cast( addingGroup ) ) ); if ( addingFilter ) { @@ -181,8 +216,21 @@ void QgsLocatorModel::addResult( const QgsLocatorResult &result ) entry.filter = result.filter; mResults << entry; } + if ( addingGroup ) + { + Entry entry; + entry.filterTitle = result.filter->displayName(); + entry.groupTitle = result.group; + // the sorting of groups will be achieved by order of adding groups + // this could be customized by adding the extra info to QgsLocatorResult + entry.groupSorting = mFoundResultsFilterGroups[result.filter].count(); + entry.filter = result.filter; + mResults << entry; + } Entry entry; entry.result = result; + // keep the group title empty to allow differecing group title from results + entry.groupSorting = result.group.isEmpty() ? NoGroup : mFoundResultsFilterGroups[result.filter].count(); mResults << entry; if ( mDeferredClear ) @@ -279,12 +327,18 @@ bool QgsLocatorProxyModel::lessThan( const QModelIndex &left, const QModelIndex if ( leftFilter != rightFilter ) return QString::localeAwareCompare( leftFilter, rightFilter ) < 0; - // then make sure filter title appears before filter's results + // then make sure filter title or group appears before filter's results int leftTypeRole = sourceModel()->data( left, QgsLocatorModel::ResultTypeRole ).toInt(); int rightTypeRole = sourceModel()->data( right, QgsLocatorModel::ResultTypeRole ).toInt(); if ( leftTypeRole != rightTypeRole ) return leftTypeRole < rightTypeRole; + // make sure group title are above + int leftGroupRole = sourceModel()->data( left, QgsLocatorModel::ResultFilterGroupSortingRole ).toInt(); + int rightGroupRole = sourceModel()->data( right, QgsLocatorModel::ResultFilterGroupSortingRole ).toInt(); + if ( leftGroupRole != rightGroupRole ) + return leftGroupRole < rightGroupRole; + // sort filter's results by score double leftScore = sourceModel()->data( left, QgsLocatorModel::ResultScoreRole ).toDouble(); double rightScore = sourceModel()->data( right, QgsLocatorModel::ResultScoreRole ).toDouble(); diff --git a/src/core/locator/qgslocatormodel.h b/src/core/locator/qgslocatormodel.h index 5544a261216..61f7ab254d1 100644 --- a/src/core/locator/qgslocatormodel.h +++ b/src/core/locator/qgslocatormodel.h @@ -45,6 +45,8 @@ class CORE_EXPORT QgsLocatorModel : public QAbstractTableModel public: + static const int NoGroup = 9999; + //! Custom model roles enum Role { @@ -53,6 +55,7 @@ class CORE_EXPORT QgsLocatorModel : public QAbstractTableModel ResultFilterPriorityRole, //!< Result priority, used by QgsLocatorProxyModel for sorting roles. 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 }; /** @@ -99,10 +102,13 @@ class CORE_EXPORT QgsLocatorModel : public QAbstractTableModel QgsLocatorResult result; QString filterTitle; QgsLocatorFilter *filter = nullptr; + QString groupTitle = QString(); + int groupSorting = 0; }; QList mResults; QSet mFoundResultsFromFilterNames; + QMap mFoundResultsFilterGroups; bool mDeferredClear = false; QTimer mDeferredClearTimer; }; diff --git a/src/gui/locator/qgslocatorwidget.cpp b/src/gui/locator/qgslocatorwidget.cpp index 1836f778121..7ee55429a82 100644 --- a/src/gui/locator/qgslocatorwidget.cpp +++ b/src/gui/locator/qgslocatorwidget.cpp @@ -252,8 +252,15 @@ void QgsLocatorWidget::addResult( const QgsLocatorResult &result ) mLocatorModel->addResult( result ); if ( selectFirst ) { - int row = mProxyModel->flags( mProxyModel->index( 0, 0 ) ) & Qt::ItemIsSelectable ? 0 : 1; - mResultsView->setCurrentIndex( mProxyModel->index( row, 0 ) ); + int row = -1; + bool selectable = false; + while ( !selectable && row < mProxyModel->rowCount() ) + { + row++; + selectable = mProxyModel->flags( mProxyModel->index( row, 0 ) ).testFlag( Qt::ItemIsSelectable ); + } + if ( selectable ) + mResultsView->setCurrentIndex( mProxyModel->index( row, 0 ) ); } }