diff --git a/python/core/qgsbrowsermodel.sip b/python/core/qgsbrowsermodel.sip index 7ba4f01f17f..e1654396c7d 100644 --- a/python/core/qgsbrowsermodel.sip +++ b/python/core/qgsbrowsermodel.sip @@ -23,6 +23,7 @@ class QgsBrowserModel : QAbstractItemModel { PathRole, CommentRole, + SortRole, }; virtual Qt::ItemFlags flags( const QModelIndex &index ) const; diff --git a/python/core/qgsdataitem.sip b/python/core/qgsdataitem.sip index 0cf12294cfb..20a80203469 100644 --- a/python/core/qgsdataitem.sip +++ b/python/core/qgsdataitem.sip @@ -271,6 +271,27 @@ Create path component replacing path separators :rtype: str %End + virtual QVariant sortKey() const; +%Docstring + Returns the sorting key for the item. By default name() is returned, + but setSortKey() can be used to set a custom sort key for the item. + + Alternatively subclasses can override this method to return a custom + sort key. + +.. seealso:: :py:func:`setSortKey()` +.. versionadded:: 3.0 + :rtype: QVariant +%End + + void setSortKey( const QVariant &key ); +%Docstring + Sets a custom sorting ``key`` for the item. +.. seealso:: :py:func:`sortKey()` +.. versionadded:: 3.0 +%End + + void setIcon( const QIcon &icon ); void setIconName( const QString &iconName ); @@ -307,6 +328,7 @@ Move object and all its descendants to thread %End + public slots: virtual void deleteLater(); @@ -669,6 +691,9 @@ Icon for favorites group :rtype: QIcon %End + virtual QVariant sortKey() const; + + }; class QgsZipItem : QgsDataCollectionItem diff --git a/src/core/qgsbrowsermodel.cpp b/src/core/qgsbrowsermodel.cpp index 7e6842b66d8..984903d6bdc 100644 --- a/src/core/qgsbrowsermodel.cpp +++ b/src/core/qgsbrowsermodel.cpp @@ -72,6 +72,7 @@ void QgsBrowserModel::updateProjectHome() mProjectHome = home.isNull() ? nullptr : new QgsDirectoryItem( nullptr, tr( "Project home" ), home, "project:" + home ); if ( mProjectHome ) { + mProjectHome->setSortKey( QStringLiteral( " 1" ) ); connectItem( mProjectHome ); beginInsertRows( QModelIndex(), 0, 0 ); @@ -84,8 +85,9 @@ void QgsBrowserModel::addRootItems() { updateProjectHome(); - // give the home directory a prominent second place + // give the home directory a prominent third place QgsDirectoryItem *item = new QgsDirectoryItem( nullptr, tr( "Home" ), QDir::homePath(), "home:" + QDir::homePath() ); + item->setSortKey( QStringLiteral( " 2" ) ); QStyle *style = QApplication::style(); QIcon homeIcon( style->standardPixmap( QStyle::SP_DirHomeIcon ) ); item->setIcon( homeIcon ); @@ -109,6 +111,7 @@ void QgsBrowserModel::addRootItems() continue; QgsDirectoryItem *item = new QgsDirectoryItem( nullptr, path, path ); + item->setSortKey( QStringLiteral( " 3 %1" ).arg( path ) ); connectItem( item ); mRootItems << item; @@ -212,6 +215,10 @@ QVariant QgsBrowserModel::data( const QModelIndex &index, int role ) const { return item->name(); } + else if ( role == QgsBrowserModel::SortRole ) + { + return item->sortKey(); + } else if ( role == Qt::ToolTipRole ) { return item->toolTip(); diff --git a/src/core/qgsbrowsermodel.h b/src/core/qgsbrowsermodel.h index ddec807a024..db790d47215 100644 --- a/src/core/qgsbrowsermodel.h +++ b/src/core/qgsbrowsermodel.h @@ -64,6 +64,7 @@ class CORE_EXPORT QgsBrowserModel : public QAbstractItemModel { PathRole = Qt::UserRole, //!< Item path used to access path in the tree, see QgsDataItem::mPath CommentRole = Qt::UserRole + 1, //!< Item comment + SortRole, //!< Custom sort role, see QgsDataItem::sortKey() }; // implemented methods from QAbstractItemModel for read-only access diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 584d7c014d7..5d80da75304 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -102,6 +102,11 @@ QIcon QgsFavoritesItem::iconFavorites() return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFavourites.png" ) ); } +QVariant QgsFavoritesItem::sortKey() const +{ + return QStringLiteral( " 0" ); +} + QIcon QgsZipItem::iconZip() { return QgsApplication::getThemeIcon( QStringLiteral( "/mIconZip.png" ) ); @@ -150,6 +155,16 @@ QString QgsDataItem::pathComponent( const QString &string ) return QString( string ).replace( QRegExp( "[\\\\/]" ), QStringLiteral( "|" ) ); } +QVariant QgsDataItem::sortKey() const +{ + return mSortKey.isValid() ? mSortKey : name(); +} + +void QgsDataItem::setSortKey( const QVariant &key ) +{ + mSortKey = key; +} + void QgsDataItem::deleteLater() { QgsDebugMsgLevel( "path = " + path(), 3 ); @@ -753,6 +768,10 @@ QVector QgsDirectoryItem::createChildren() continue; QgsDirectoryItem *item = new QgsDirectoryItem( this, subdir, subdirPath, path ); + + // we want directories shown before files + item->setSortKey( QStringLiteral( " %1" ).arg( subdir ) ); + // propagate signals up to top children.append( item ); diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index 0e9405d1467..bf4796e9eee 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -255,6 +255,26 @@ class CORE_EXPORT QgsDataItem : public QObject //! Create path component replacing path separators static QString pathComponent( const QString &component ); + /** + * Returns the sorting key for the item. By default name() is returned, + * but setSortKey() can be used to set a custom sort key for the item. + * + * Alternatively subclasses can override this method to return a custom + * sort key. + * + * \see setSortKey() + * \since QGIS 3.0 + */ + virtual QVariant sortKey() const; + + /** + * Sets a custom sorting \a key for the item. + * \see sortKey() + * \since QGIS 3.0 + */ + void setSortKey( const QVariant &key ); + + // Because QIcon (QPixmap) must not be used in outside the GUI thread, it is // not possible to set mIcon in constructor. Either use mIconName/setIconName() // or implement icon(). @@ -303,6 +323,9 @@ class CORE_EXPORT QgsDataItem : public QObject QIcon mIcon; QMap mIconMap; + //! Custom sort key. If invalid, name() will be used for sorting instead. + QVariant mSortKey; + public slots: /** @@ -631,6 +654,8 @@ class CORE_EXPORT QgsFavoritesItem : public QgsDataCollectionItem //! Icon for favorites group static QIcon iconFavorites(); + QVariant sortKey() const override; + private: QVector createChildren( const QString &favDir ); }; diff --git a/src/gui/qgsbrowserdockwidget.cpp b/src/gui/qgsbrowserdockwidget.cpp index 39e9bebc6ae..9b5dd54f371 100644 --- a/src/gui/qgsbrowserdockwidget.cpp +++ b/src/gui/qgsbrowserdockwidget.cpp @@ -122,6 +122,8 @@ void QgsBrowserDockWidget::showEvent( QShowEvent *e ) mBrowserView->setSettingsSection( objectName().toLower() ); // to distinguish 2 or more instances of the browser mBrowserView->setBrowserModel( mModel ); mBrowserView->setModel( mProxyModel ); + mBrowserView->setSortingEnabled( true ); + mBrowserView->sortByColumn( 0, Qt::AscendingOrder ); // provide a horizontal scroll bar instead of using ellipse (...) for longer items mBrowserView->setTextElideMode( Qt::ElideNone ); mBrowserView->header()->setSectionResizeMode( 0, QHeaderView::ResizeToContents ); diff --git a/src/gui/qgsbrowserdockwidget_p.cpp b/src/gui/qgsbrowserdockwidget_p.cpp index 454b3e9d4a9..625e590e60a 100644 --- a/src/gui/qgsbrowserdockwidget_p.cpp +++ b/src/gui/qgsbrowserdockwidget_p.cpp @@ -339,6 +339,9 @@ QgsBrowserTreeFilterProxyModel::QgsBrowserTreeFilterProxyModel( QObject *parent , mCaseSensitivity( Qt::CaseInsensitive ) { setDynamicSortFilter( true ); + setSortRole( QgsBrowserModel::SortRole ); + setSortCaseSensitivity( Qt::CaseInsensitive ); + sort( 0 ); } void QgsBrowserTreeFilterProxyModel::setBrowserModel( QgsBrowserModel *model )