Skip to content

Commit

Permalink
[FEATURE][layouts] Add search box to layout manager
Browse files Browse the repository at this point in the history
Allows filtering the list of layouts in a project by name, handy
when a project has many layouts
  • Loading branch information
nyalldawson committed Nov 20, 2019
1 parent f1e6745 commit 1ca926d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 26 deletions.
21 changes: 21 additions & 0 deletions python/core/auto_generated/layout/qgslayoutmanager.sip.in
Expand Up @@ -264,6 +264,27 @@ Returns the current filters used for filtering available layouts.
Sets the current ``filters`` used for filtering available layouts.

.. seealso:: :py:func:`filters`
%End

QString filterString() const;
%Docstring
Returns the current filter string, if set.

.. seealso:: :py:func:`setFilterString`

.. versionadded:: 3.12
%End

public slots:

void setFilterString( const QString &filter );
%Docstring
Sets a ``filter`` string, such that only layouts with names containing the
specified string will be shown.

.. seealso:: :py:func:`filterString`

.. versionadded:: 3.12
%End

};
Expand Down
5 changes: 5 additions & 0 deletions src/app/layout/qgslayoutmanagerdialog.cpp
Expand Up @@ -71,6 +71,11 @@ QgsLayoutManagerDialog::QgsLayoutManagerDialog( QWidget *parent, Qt::WindowFlags
mProxyModel->setSourceModel( mModel );
mLayoutListView->setModel( mProxyModel );

mSearchLineEdit->setShowSearchIcon( true );
mSearchLineEdit->setShowClearButton( true );
mSearchLineEdit->setFocus();
connect( mSearchLineEdit, &QgsFilterLineEdit::textChanged, mProxyModel, &QgsLayoutManagerProxyModel::setFilterString );

connect( mButtonBox, &QDialogButtonBox::rejected, this, &QWidget::close );
connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsLayoutManagerDialog::showHelp );
connect( mLayoutListView->selectionModel(), &QItemSelectionModel::selectionChanged,
Expand Down
12 changes: 12 additions & 0 deletions src/core/layout/qgslayoutmanager.cpp
Expand Up @@ -562,6 +562,12 @@ bool QgsLayoutManagerProxyModel::filterAcceptsRow( int sourceRow, const QModelIn
if ( !layout )
return model->allowEmptyLayout();

if ( !mFilterString.trimmed().isEmpty() )
{
if ( !layout->name().contains( mFilterString, Qt::CaseInsensitive ) )
return false;
}

switch ( layout->layoutType() )
{
case QgsMasterLayoutInterface::PrintLayout:
Expand All @@ -582,3 +588,9 @@ void QgsLayoutManagerProxyModel::setFilters( Filters filters )
mFilters = filters;
invalidateFilter();
}

void QgsLayoutManagerProxyModel::setFilterString( const QString &filter )
{
mFilterString = filter;
invalidateFilter();
}
21 changes: 21 additions & 0 deletions src/core/layout/qgslayoutmanager.h
Expand Up @@ -271,9 +271,30 @@ class CORE_EXPORT QgsLayoutManagerProxyModel : public QSortFilterProxyModel
*/
void setFilters( QgsLayoutManagerProxyModel::Filters filters );

/**
* Returns the current filter string, if set.
*
* \see setFilterString()
* \since QGIS 3.12
*/
QString filterString() const { return mFilterString; }

public slots:

/**
* Sets a \a filter string, such that only layouts with names containing the
* specified string will be shown.
*
* \see filterString()
* \since QGIS 3.12
*/
void setFilterString( const QString &filter );

private:

Filters mFilters = Filters( FilterPrintLayouts | FilterReports );

QString mFilterString;
};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayoutManagerProxyModel::Filters )
Expand Down
69 changes: 46 additions & 23 deletions src/ui/layout/qgslayoutmanagerbase.ui
Expand Up @@ -25,66 +25,83 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QToolButton" name="mDuplicateButton">
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0">
<item row="1" column="2">
<widget class="QToolButton" name="mRemoveButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Duplicate…</string>
<string>&amp;Remove…</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QToolButton" name="mRenameButton">
<item row="0" column="0" colspan="4">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QgsFilterLineEdit" name="mSearchLineEdit">
<property name="placeholderText">
<string>Search…</string>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="mLayoutListView">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QToolButton" name="mShowButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Re&amp;name…</string>
<string>&amp;Show</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="mRemoveButton">
<item row="1" column="3">
<widget class="QToolButton" name="mRenameButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Remove…</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QListView" name="mLayoutListView">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
<string>Re&amp;name…</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QToolButton" name="mShowButton">
<item row="1" column="1">
<widget class="QToolButton" name="mDuplicateButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Show</string>
<string>&amp;Duplicate…</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -215,8 +232,14 @@
<header>qgsfilewidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsFilterLineEdit</class>
<extends>QLineEdit</extends>
<header>qgsfilterlineedit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mSearchLineEdit</tabstop>
<tabstop>mLayoutListView</tabstop>
<tabstop>mShowButton</tabstop>
<tabstop>mDuplicateButton</tabstop>
Expand Down
39 changes: 36 additions & 3 deletions tests/src/python/test_qgslayoutmanagermodel.py
Expand Up @@ -187,7 +187,7 @@ def testProxyModel(self):
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)

r = QgsReport(project)
r.setName('ddd')
r.setName('bbb2')
manager.addLayout(r)
self.assertEqual(proxy.rowCount(QModelIndex()), 4)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
Expand All @@ -196,9 +196,23 @@ def testProxyModel(self):
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
self.assertEqual(proxy.data(proxy.index(3, 0, QModelIndex()), Qt.DisplayRole), 'ddd')
self.assertEqual(proxy.data(proxy.index(3, 0, QModelIndex()), Qt.DisplayRole), 'bbb2')
self.assertEqual(proxy.data(proxy.index(3, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), r)

proxy.setFilterString('xx')
self.assertEqual(proxy.rowCount(QModelIndex()), 1)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
proxy.setFilterString('bb')
self.assertEqual(proxy.rowCount(QModelIndex()), 3)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole), 'bbb2')
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), r)
proxy.setFilterString('')

proxy.setFilters(QgsLayoutManagerProxyModel.FilterPrintLayouts)
self.assertEqual(proxy.filters(), QgsLayoutManagerProxyModel.FilterPrintLayouts)
self.assertEqual(proxy.rowCount(QModelIndex()), 3)
Expand All @@ -208,15 +222,34 @@ def testProxyModel(self):
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
proxy.setFilterString('bb')
self.assertEqual(proxy.rowCount(QModelIndex()), 2)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
proxy.setFilterString('')

proxy.setFilters(QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.filters(), QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.rowCount(QModelIndex()), 2)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'ddd')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'bbb2')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), r)

proxy.setFilterString('bb')
self.assertEqual(proxy.rowCount(QModelIndex()), 2)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'bbb2')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), r)
proxy.setFilterString('aaa')
self.assertEqual(proxy.rowCount(QModelIndex()), 1)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
proxy.setFilterString('')

proxy.setFilters(QgsLayoutManagerProxyModel.FilterPrintLayouts | QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.filters(), QgsLayoutManagerProxyModel.FilterPrintLayouts | QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.rowCount(QModelIndex()), 4)
Expand Down

0 comments on commit 1ca926d

Please sign in to comment.