QgsDataSourceSelectDialog: filter, refresh and scroll to last selected item

plus:
- expand children of last selected item
- save/restore status
This commit is contained in:
Alessandro Pasotti 2018-11-07 11:08:49 +01:00
parent 4cdde31c17
commit 02ecb56d95
4 changed files with 380 additions and 5 deletions

View File

@ -55,6 +55,28 @@ Sets layer type filter to ``layerType`` and activates the filtering
QgsMimeDataUtils::Uri uri() const;
%Docstring
Returns the (possibly invalid) uri of the selected data source
%End
void showFilterWidget( bool visible );
%Docstring
Show/hide filter widget
%End
void setFilterSyntax( QAction * );
%Docstring
Sets filter syntax
%End
void setCaseSensitive( bool caseSensitive );
%Docstring
Sets filter case sensitivity
%End
void setFilter();
%Docstring
Apply filter to the model
%End
virtual void showEvent( QShowEvent *e );
%Docstring
Scroll to last selected index and expand it's children
%End
};

View File

@ -19,8 +19,10 @@
#include "qgssettings.h"
#include "qgsgui.h"
#include "qgis.h"
#include "qgsbrowsermodel.h"
#include <QPushButton>
#include <QMenu>
QgsDataSourceSelectDialog::QgsDataSourceSelectDialog(
QgsBrowserModel *browserModel,
@ -32,6 +34,7 @@ QgsDataSourceSelectDialog::QgsDataSourceSelectDialog(
if ( ! browserModel )
{
mBrowserModel = qgis::make_unique<QgsBrowserModel>();
mBrowserModel->initialize();
mOwnModel = true;
}
else
@ -44,12 +47,12 @@ QgsDataSourceSelectDialog::QgsDataSourceSelectDialog(
setWindowTitle( tr( "Select a Data Source" ) );
QgsGui::enableAutoGeometryRestore( this );
mBrowserModel->initialize();
mBrowserProxyModel.setBrowserModel( mBrowserModel.get() );
mBrowserTreeView->setHeaderHidden( true );
if ( setFilterByLayerType )
{
// This will also set the (proxy) model
setLayerTypeFilter( layerType );
}
else
@ -57,7 +60,55 @@ QgsDataSourceSelectDialog::QgsDataSourceSelectDialog(
mBrowserTreeView->setModel( &mBrowserProxyModel );
buttonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( false );
}
mBrowserTreeView->setBrowserModel( mBrowserModel.get() );
mWidgetFilter->hide();
mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
// icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
QMenu *menu = new QMenu( this );
menu->setSeparatorsCollapsible( false );
mBtnFilterOptions->setMenu( menu );
QAction *action = new QAction( tr( "Case Sensitive" ), menu );
action->setData( "case" );
action->setCheckable( true );
action->setChecked( false );
connect( action, &QAction::toggled, this, &QgsDataSourceSelectDialog::setCaseSensitive );
menu->addAction( action );
QActionGroup *group = new QActionGroup( menu );
action = new QAction( tr( "Filter Pattern Syntax" ), group );
action->setSeparator( true );
menu->addAction( action );
action = new QAction( tr( "Normal" ), group );
action->setData( QgsBrowserProxyModel::Normal );
action->setCheckable( true );
action->setChecked( true );
menu->addAction( action );
action = new QAction( tr( "Wildcard(s)" ), group );
action->setData( QgsBrowserProxyModel::Wildcards );
action->setCheckable( true );
menu->addAction( action );
action = new QAction( tr( "Regular Expression" ), group );
action->setData( QgsBrowserProxyModel::RegularExpression );
action->setCheckable( true );
menu->addAction( action );
mBrowserTreeView->setExpandsOnDoubleClick( false );
connect( mActionRefresh, &QAction::triggered, [ = ] { refreshModel( QModelIndex() ); } );
connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, &QgsDataSourceSelectDialog::onLayerSelected );
connect( mActionCollapse, &QAction::triggered, mBrowserTreeView, &QgsBrowserTreeView::collapseAll );
connect( mActionShowFilter, &QAction::triggered, this, &QgsDataSourceSelectDialog::showFilterWidget );
connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsDataSourceSelectDialog::setFilter );
connect( mLeFilter, &QgsFilterLineEdit::cleared, this, &QgsDataSourceSelectDialog::setFilter );
connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsDataSourceSelectDialog::setFilter );
connect( group, &QActionGroup::triggered, this, &QgsDataSourceSelectDialog::setFilterSyntax );
if ( QgsSettings().value( QStringLiteral( "datasourceSelectFilterVisible" ), false, QgsSettings::Section::Gui ).toBool() )
{
mActionShowFilter->trigger();
}
}
QgsDataSourceSelectDialog::~QgsDataSourceSelectDialog()
@ -66,6 +117,107 @@ QgsDataSourceSelectDialog::~QgsDataSourceSelectDialog()
mBrowserModel.release();
}
void QgsDataSourceSelectDialog::showEvent( QShowEvent *e )
{
QDialog::showEvent( e );
QString lastSelectedPath( QgsSettings().value( QStringLiteral( "datasourceSelectLastSelectedItem" ),
QString(), QgsSettings::Section::Gui ).toString() );
if ( ! lastSelectedPath.isEmpty() )
{
QModelIndexList items = mBrowserProxyModel.match(
mBrowserProxyModel.index( 0, 0 ),
QgsBrowserModel::PathRole,
QVariant::fromValue( lastSelectedPath ),
1,
Qt::MatchRecursive );
if ( items.count( ) > 0 )
{
QModelIndex expandIndex = items.at( 0 );
if ( expandIndex.isValid() )
{
mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::QgsBrowserTreeView::ScrollHint::PositionAtTop );
mBrowserTreeView->expand( expandIndex );
}
}
}
}
void QgsDataSourceSelectDialog::showFilterWidget( bool visible )
{
QgsSettings().setValue( QStringLiteral( "datasourceSelectFilterVisible" ), visible, QgsSettings::Section::Gui );
mWidgetFilter->setVisible( visible );
if ( ! visible )
{
mLeFilter->setText( QString() );
setFilter();
}
else
{
mLeFilter->setFocus();
}
}
void QgsDataSourceSelectDialog::setFilter()
{
QString filter = mLeFilter->text();
mBrowserProxyModel.setFilterString( filter );
}
void QgsDataSourceSelectDialog::refreshModel( const QModelIndex &index )
{
QgsDataItem *item = mBrowserModel->dataItem( index );
if ( item )
{
QgsDebugMsg( "path = " + item->path() );
}
else
{
QgsDebugMsg( QStringLiteral( "invalid item" ) );
}
if ( item && ( item->capabilities2() & QgsDataItem::Fertile ) )
{
mBrowserModel->refresh( index );
}
for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
{
QModelIndex idx = mBrowserModel->index( i, 0, index );
QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
QgsDataItem *child = mBrowserModel->dataItem( idx );
// Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
// Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & QgsDataItem::Fast ) )
{
refreshModel( idx );
}
else
{
if ( child && ( child->capabilities2() & QgsDataItem::Fertile ) )
{
child->depopulate();
}
}
}
}
void QgsDataSourceSelectDialog::setFilterSyntax( QAction *action )
{
if ( !action )
return;
mBrowserProxyModel.setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
}
void QgsDataSourceSelectDialog::setCaseSensitive( bool caseSensitive )
{
mBrowserProxyModel.setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
}
void QgsDataSourceSelectDialog::setLayerTypeFilter( QgsMapLayer::LayerType layerType )
{
mBrowserProxyModel.setFilterByLayerType( true );
@ -95,6 +247,8 @@ void QgsDataSourceSelectDialog::onLayerSelected( const QModelIndex &index )
{
isLayerCompatible = true;
mUri = layerItem->mimeUri();
// Store last viewed item
QgsSettings().setValue( QStringLiteral( "datasourceSelectLastSelectedItem" ), mBrowserProxyModel.data( index, QgsBrowserModel::PathRole ).toString(), QgsSettings::Section::Gui );
}
}
}

View File

@ -73,6 +73,17 @@ class GUI_EXPORT QgsDataSourceSelectDialog: public QDialog, private Ui::QgsDataS
*/
QgsMimeDataUtils::Uri uri() const;
//! Show/hide filter widget
void showFilterWidget( bool visible );
//! Sets filter syntax
void setFilterSyntax( QAction * );
//! Sets filter case sensitivity
void setCaseSensitive( bool caseSensitive );
//! Apply filter to the model
void setFilter();
//! Scroll to last selected index and expand it's children
void showEvent( QShowEvent *e ) override;
private slots:
//! Triggered when a layer is selected in the browser
@ -80,6 +91,9 @@ class GUI_EXPORT QgsDataSourceSelectDialog: public QDialog, private Ui::QgsDataS
private:
//! Refresh the model
void refreshModel( const QModelIndex &index );
QgsBrowserProxyModel mBrowserProxyModel;
std::unique_ptr<QgsBrowserModel> mBrowserModel;
bool mOwnModel = true;

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>700</width>
<height>629</height>
</rect>
</property>
<property name="windowTitle">
@ -15,7 +15,143 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QgsBrowserTreeView" name="mBrowserTreeView"/>
<widget class="QWidget" name="mContents">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolBar" name="mBrowserToolbar">
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="floatable">
<bool>false</bool>
</property>
<addaction name="mActionRefresh"/>
<addaction name="mActionShowFilter"/>
<addaction name="mActionCollapse"/>
</widget>
</item>
<item>
<widget class="QWidget" name="mWidgetFilter" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="mBtnFilterOptions">
<property name="toolTip">
<string>Options</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionOptions.svg</normaloff>:/images/themes/default/mActionOptions.svg</iconset>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QgsFilterLineEdit" name="mLeFilter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QgsBrowserTreeView" name="mBrowserTreeView"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
@ -28,15 +164,64 @@
</widget>
</item>
</layout>
<action name="mActionRefresh">
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionRefresh.svg</normaloff>:/images/themes/default/mActionRefresh.svg</iconset>
</property>
<property name="text">
<string>Refresh</string>
</property>
</action>
<action name="mActionShowFilter">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionFilter2.svg</normaloff>:/images/themes/default/mActionFilter2.svg</iconset>
</property>
<property name="text">
<string>Filter Browser</string>
</property>
<property name="iconText">
<string>Filter Browser</string>
</property>
<property name="toolTip">
<string>Filter Browser</string>
</property>
</action>
<action name="mActionCollapse">
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionCollapseTree.svg</normaloff>:/images/themes/default/mActionCollapseTree.svg</iconset>
</property>
<property name="text">
<string>Collapse All</string>
</property>
<property name="toolTip">
<string>Collapse All</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>QgsFilterLineEdit</class>
<extends>QLineEdit</extends>
<header>qgsfilterlineedit.h</header>
</customwidget>
<customwidget>
<class>QgsBrowserTreeView</class>
<extends>QTreeView</extends>
<header>qgsbrowsertreeview.h</header>
</customwidget>
</customwidgets>
<resources/>
<resources>
<include location="../../images/images.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>