diff --git a/python/gui/auto_generated/processing/qgsprocessingtoolboxmodel.sip.in b/python/gui/auto_generated/processing/qgsprocessingtoolboxmodel.sip.in index 0708ebc08ac..dd2f726a5a7 100644 --- a/python/gui/auto_generated/processing/qgsprocessingtoolboxmodel.sip.in +++ b/python/gui/auto_generated/processing/qgsprocessingtoolboxmodel.sip.in @@ -346,6 +346,13 @@ Returns the index corresponding to the specified ``providerId``. QModelIndex indexOfParentTreeNode( QgsProcessingToolboxModelNode *parentNode ) const; %Docstring Returns the index corresponding to the parent of a given node. +%End + + signals: + + void recentAlgorithmAdded(); +%Docstring +Emitted whenever recent algorithms are added to the model. %End }; diff --git a/src/gui/processing/qgsprocessingtoolboxmodel.cpp b/src/gui/processing/qgsprocessingtoolboxmodel.cpp index cc768266c53..00b1a980c8c 100644 --- a/src/gui/processing/qgsprocessingtoolboxmodel.cpp +++ b/src/gui/processing/qgsprocessingtoolboxmodel.cpp @@ -135,7 +135,7 @@ void QgsProcessingToolboxModel::rebuild() std::unique_ptr< QgsProcessingToolboxModelRecentNode > recentNode = qgis::make_unique< QgsProcessingToolboxModelRecentNode >(); mRecentNode = recentNode.get(); mRootNode->addChildNode( recentNode.release() ); - repopulateRecentAlgorithms(); + repopulateRecentAlgorithms( true ); } const QList< QgsProcessingProvider * > providers = mRegistry->providers(); @@ -171,7 +171,10 @@ void QgsProcessingToolboxModel::repopulateRecentAlgorithms( bool resetting ) } if ( recentAlgorithms.empty() ) + { + emit recentAlgorithmAdded(); return; + } if ( !resetting ) { @@ -180,7 +183,7 @@ void QgsProcessingToolboxModel::repopulateRecentAlgorithms( bool resetting ) for ( const QgsProcessingAlgorithm *algorithm : qgis::as_const( recentAlgorithms ) ) { - std::unique_ptr< QgsProcessingToolboxModelAlgorithmNode > algorithmNode = qgis::make_unique< QgsProcessingToolboxModelAlgorithmNode >( algorithm, mRegistry ); + std::unique_ptr< QgsProcessingToolboxModelAlgorithmNode > algorithmNode = qgis::make_unique< QgsProcessingToolboxModelAlgorithmNode >( algorithm ); mRecentNode->addChildNode( algorithmNode.release() ); } @@ -188,7 +191,7 @@ void QgsProcessingToolboxModel::repopulateRecentAlgorithms( bool resetting ) { endInsertRows(); } - + emit recentAlgorithmAdded(); } void QgsProcessingToolboxModel::providerAdded( const QString &id ) @@ -615,6 +618,8 @@ QgsProcessingToolboxProxyModel::QgsProcessingToolboxProxyModel( QObject *parent, setSortLocaleAware( true ); setFilterCaseSensitivity( Qt::CaseInsensitive ); sort( 0 ); + + connect( mModel, &QgsProcessingToolboxModel::recentAlgorithmAdded, this, [ = ] { invalidateFilter(); } ); } void QgsProcessingToolboxProxyModel::setFilters( QgsProcessingToolboxProxyModel::Filters filters ) @@ -701,8 +706,6 @@ bool QgsProcessingToolboxProxyModel::filterAcceptsRow( int sourceRow, const QMod } } - bool isRecentNode = mModel->data( sourceIndex, QgsProcessingToolboxModel::RoleNodeType ).toInt() == QgsProcessingToolboxModelNode::NodeRecent; - if ( QgsProcessingProvider *provider = mModel->providerForIndex( sourceIndex ) ) { return hasChildren && provider->isActive(); @@ -735,6 +738,23 @@ bool QgsProcessingToolboxProxyModel::lessThan( const QModelIndex &left, const QM return true; } + // if node represents a recent algorithm, it's not sorted at all + bool isRecentNode = false; + QModelIndex parent = left.parent(); + while ( parent.isValid() ) + { + if ( mModel->data( parent, QgsProcessingToolboxModel::RoleNodeType ).toInt() == QgsProcessingToolboxModelNode::NodeRecent ) + { + isRecentNode = true; + break; + } + } + if ( isRecentNode ) + { + return left.row() < right.row(); + } + + // default mode is alphabetical order QString leftStr = sourceModel()->data( left ).toString(); QString rightStr = sourceModel()->data( right ).toString(); diff --git a/src/gui/processing/qgsprocessingtoolboxmodel.h b/src/gui/processing/qgsprocessingtoolboxmodel.h index 92178ff47e8..7cdffadcece 100644 --- a/src/gui/processing/qgsprocessingtoolboxmodel.h +++ b/src/gui/processing/qgsprocessingtoolboxmodel.h @@ -359,6 +359,13 @@ class GUI_EXPORT QgsProcessingToolboxModel : public QAbstractItemModel */ QModelIndex indexOfParentTreeNode( QgsProcessingToolboxModelNode *parentNode ) const; + signals: + + /** + * Emitted whenever recent algorithms are added to the model. + */ + void recentAlgorithmAdded(); + private slots: void rebuild(); diff --git a/tests/src/gui/testqgsprocessingmodel.cpp b/tests/src/gui/testqgsprocessingmodel.cpp index 87a464b4c25..4d946c40fdc 100644 --- a/tests/src/gui/testqgsprocessingmodel.cpp +++ b/tests/src/gui/testqgsprocessingmodel.cpp @@ -519,18 +519,34 @@ void TestQgsProcessingModel::testProxyModel() QCOMPARE( model.rowCount(), 4 ); // check sort order of recent algorithms - recentLog.push( QStringLiteral( "qgis:a1" ) ); + recentLog.push( QStringLiteral( "p2:a1" ) ); QCOMPARE( model.rowCount(), 5 ); QModelIndex recentIndex = model.index( 0, 0, QModelIndex() ); QCOMPARE( model.data( recentIndex, Qt::DisplayRole ).toString(), QStringLiteral( "Recently used" ) ); + QCOMPARE( model.rowCount( recentIndex ), 1 ); + QCOMPARE( model.data( model.index( 0, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p2:a1" ) ); + recentLog.push( QStringLiteral( "p1:a2" ) ); + QCOMPARE( model.rowCount( recentIndex ), 2 ); + QCOMPARE( model.data( model.index( 0, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p1:a2" ) ); + QCOMPARE( model.data( model.index( 1, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p2:a1" ) ); + recentLog.push( QStringLiteral( "qgis:a1" ) ); + QCOMPARE( model.rowCount( recentIndex ), 3 ); + QCOMPARE( model.data( model.index( 0, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "qgis:a1" ) ); + QCOMPARE( model.data( model.index( 1, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p1:a2" ) ); + QCOMPARE( model.data( model.index( 2, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p2:a1" ) ); + recentLog.push( QStringLiteral( "p2:a1" ) ); + QCOMPARE( model.rowCount( recentIndex ), 3 ); + QCOMPARE( model.data( model.index( 0, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p2:a1" ) ); + QCOMPARE( model.data( model.index( 1, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "qgis:a1" ) ); + QCOMPARE( model.data( model.index( 2, 0, recentIndex ), QgsProcessingToolboxModel::RoleAlgorithmId ).toString(), QStringLiteral( "p1:a2" ) ); // inactive provider - should not be visible - QCOMPARE( model.rowCount(), 4 ); + QCOMPARE( model.rowCount(), 5 ); DummyAlgorithm *qgisA31 = new DummyAlgorithm( "a3", "group1" ); DummyProvider *p3 = new DummyProvider( "p3", "provider3", QList< QgsProcessingAlgorithm * >() << qgisA31 ); p3->mActive = false; registry.addProvider( p3 ); - QCOMPARE( model.rowCount(), 4 ); + QCOMPARE( model.rowCount(), 5 ); } QGSTEST_MAIN( TestQgsProcessingModel )