Factories can specify a path to add their pages to the options tree

This commit is contained in:
Nyall Dawson 2021-07-29 13:55:35 +10:00
parent 518efd0850
commit 550a1d3a52
9 changed files with 186 additions and 17 deletions

View File

@ -38,6 +38,9 @@ class ConsoleOptionsFactory(QgsOptionsWidgetFactory):
def icon(self):
return QgsApplication.getThemeIcon('/console/mIconRunConsole.svg')
def path(self):
return ['ide']
def createWidget(self, parent):
return ConsoleOptionsPage(parent)

View File

@ -96,7 +96,7 @@ Sets the dialog ``page`` (by object name) to show.
.. versionadded:: 3.14
%End
void addPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget /Transfer/ );
void addPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget /Transfer/, const QStringList &path = QStringList() );
%Docstring
Adds a new page to the dialog pages.
@ -104,12 +104,15 @@ The ``title``, ``tooltip`` and ``icon`` arguments dictate the page list item tit
The page content is specified via the ``widget`` argument. Ownership of ``widget`` is transferred to the dialog.
Since QGIS 3.22, the optional ``path`` argument can be used to set the path of the item's entry in the tree view
(for dialogs which show a tree view of options pages only).
.. seealso:: :py:func:`insertPage`
.. versionadded:: 3.14
%End
void insertPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget /Transfer/, const QString &before );
void insertPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget /Transfer/, const QString &before, const QStringList &path = QStringList() );
%Docstring
Inserts a new page into the dialog pages.
@ -120,6 +123,9 @@ The page content is specified via the ``widget`` argument. Ownership of ``widget
The ``before`` argument specifies the object name of an existing page. The new page will be inserted directly
before the matching page.
Since QGIS 3.22, the optional ``path`` argument can be used to set the path of the item's entry in the tree view
(for dialogs which show a tree view of options pages only).
.. seealso:: :py:func:`addPage`
.. versionadded:: 3.14

View File

@ -120,6 +120,17 @@ The default implementation returns an empty string, which causes the widget
to be placed at the end of the dialog page list.
.. versionadded:: 3.18
%End
virtual QStringList path() const;
%Docstring
Returns the path to place the widget page at, for options dialogs
which are structured using a tree view.
A factory which returns "Code", "Javascript" would have its widget placed
in a group named "Javascript", contained in a parent group named "Code".
.. versionadded:: 3.22
%End
virtual QgsOptionsPageWidget *createWidget( QWidget *parent = 0 ) const = 0 /Factory/;

View File

@ -356,3 +356,13 @@ QgsOptionsPageWidget *QgsCodeEditorOptionsFactory::createWidget( QWidget *parent
{
return new QgsCodeEditorOptionsWidget( parent );
}
QStringList QgsCodeEditorOptionsFactory::path() const
{
return {QStringLiteral( "ide" ) };
}
QString QgsCodeEditorOptionsFactory::pagePositionHint() const
{
return QStringLiteral( "consoleOptions" );
}

View File

@ -63,6 +63,8 @@ class QgsCodeEditorOptionsFactory : public QgsOptionsWidgetFactory
QIcon icon() const override;
QgsOptionsPageWidget *createWidget( QWidget *parent = nullptr ) const override;
QStringList path() const override;
QString pagePositionHint() const override;
};

View File

@ -110,6 +110,7 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mTreeModel->appendRow( createItem( tr( "System" ), tr( "System" ), QStringLiteral( "propertyicons/system.svg" ) ) );
QStandardItem *crsGroup = new QStandardItem( tr( "CRS and Transforms" ) );
crsGroup->setToolTip( tr( "CRS and Transforms" ) );
crsGroup->setSelectable( false );
crsGroup->appendRow( createItem( tr( "CRS" ), tr( "CRS" ), QStringLiteral( "propertyicons/CRS.svg" ) ) );
crsGroup->appendRow( createItem( tr( "Transformations" ), tr( "Coordinate transformations and operations" ), QStringLiteral( "transformation.svg" ) ) );
@ -131,6 +132,12 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mTreeModel->appendRow( createItem( tr( "Locator" ), tr( "Locator" ), QStringLiteral( "search.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Acceleration" ), tr( "GPU acceleration" ), QStringLiteral( "mIconGPU.svg" ) ) );
QStandardItem *ideGroup = new QStandardItem( tr( "IDE" ) );
ideGroup->setData( QStringLiteral( "ide" ) );
ideGroup->setToolTip( tr( "Development and Scripting Settings" ) );
ideGroup->setSelectable( false );
mTreeModel->appendRow( ideGroup );
mOptionsTreeView->setModel( mTreeModel );
connect( cbxProjectDefaultNew, &QCheckBox::toggled, this, &QgsOptions::cbxProjectDefaultNew_toggled );
@ -1259,9 +1266,9 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mAdditionalOptionWidgets << page;
const QString beforePage = factory->pagePositionHint();
if ( beforePage.isEmpty() )
addPage( factory->title(), factory->title(), factory->icon(), page );
addPage( factory->title(), factory->title(), factory->icon(), page, factory->path() );
else
insertPage( factory->title(), factory->title(), factory->icon(), page, beforePage );
insertPage( factory->title(), factory->title(), factory->icon(), page, beforePage, factory->path() );
if ( QgsAdvancedSettingsWidget *advancedPage = qobject_cast< QgsAdvancedSettingsWidget * >( page ) )
{
@ -1361,6 +1368,8 @@ QgsOptions::~QgsOptions()
void QgsOptions::checkPageWidgetNameMap()
{
return;
const QMap< QString, int > pageNames = QgisApp::instance()->optionsPagesMap();
int pageCount = 0;

View File

@ -320,13 +320,15 @@ void QgsOptionsDialogBase::setCurrentPage( const QString &page )
}
}
void QgsOptionsDialogBase::addPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget )
void QgsOptionsDialogBase::addPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QStringList &path )
{
QListWidgetItem *item = new QListWidgetItem();
item->setIcon( icon );
item->setText( title );
item->setToolTip( tooltip );
int newPage = -1;
if ( mOptListWidget )
{
mOptListWidget->addItem( item );
@ -335,18 +337,70 @@ void QgsOptionsDialogBase::addPage( const QString &title, const QString &tooltip
{
QStandardItem *item = new QStandardItem( icon, title );
item->setToolTip( tooltip );
mOptTreeModel->appendRow( item );
QModelIndex parent;
QStandardItem *parentItem = nullptr;
if ( !path.empty() )
{
QStringList parents = path;
while ( !parents.empty() )
{
const QString parentPath = parents.takeFirst();
QModelIndex thisParent;
for ( int row = 0; row < mOptTreeModel->rowCount( parent ); ++row )
{
const QModelIndex index = mOptTreeModel->index( row, 0, parent );
if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0
|| index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
{
thisParent = index;
break;
}
}
// add new child if required
if ( !thisParent.isValid() )
{
QStandardItem *newParentItem = new QStandardItem( parentPath );
newParentItem->setToolTip( parentPath );
newParentItem->setSelectable( false );
if ( parentItem )
parentItem->appendRow( newParentItem );
else
mOptTreeModel->appendRow( newParentItem );
parentItem = newParentItem;
}
else
{
parentItem = mOptTreeModel->itemFromIndex( thisParent );
}
parent = mOptTreeModel->indexFromItem( parentItem );
}
}
if ( parentItem )
{
parentItem->appendRow( item );
const QModelIndex newIndex = mOptTreeModel->indexFromItem( item );
newPage = mTreeProxyModel->sourceIndexToPageNumber( newIndex );
}
else
mOptTreeModel->appendRow( item );
}
mOptStackedWidget->addWidget( widget );
if ( newPage < 0 )
mOptStackedWidget->addWidget( widget );
else
mOptStackedWidget->insertWidget( newPage, widget );
}
void QgsOptionsDialogBase::insertPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QString &before )
void QgsOptionsDialogBase::insertPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QString &before, const QStringList &path )
{
//find the page with a matching widget name
for ( int idx = 0; idx < mOptStackedWidget->count(); ++idx )
for ( int page = 0; page < mOptStackedWidget->count(); ++page )
{
QWidget *currentPage = mOptStackedWidget->widget( idx );
QWidget *currentPage = mOptStackedWidget->widget( page );
if ( currentPage->objectName() == before )
{
//found the "before" page
@ -358,26 +412,83 @@ void QgsOptionsDialogBase::insertPage( const QString &title, const QString &tool
if ( mOptListWidget )
{
mOptListWidget->insertItem( idx, item );
mOptListWidget->insertItem( page, item );
}
else if ( mOptTreeModel )
{
QModelIndex sourceIndexBefore = mTreeProxyModel->pageNumberToSourceIndex( idx );
QModelIndex sourceIndexBefore = mTreeProxyModel->pageNumberToSourceIndex( page );
QList< QModelIndex > sourceBeforeIndices;
while ( sourceIndexBefore.parent().isValid() )
{
sourceBeforeIndices.insert( 0, sourceIndexBefore );
sourceIndexBefore = sourceIndexBefore.parent();
}
sourceBeforeIndices.insert( 0, sourceIndexBefore );
QStringList parentPaths = path;
QModelIndex parentIndex;
QStandardItem *parentItem = nullptr;
while ( !parentPaths.empty() )
{
QString thisPath = parentPaths.takeFirst();
QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0
|| sourceIndex.data( Qt::UserRole + 1 ).toString().compare( thisPath, Qt::CaseInsensitive ) == 0 )
{
parentIndex = sourceIndex;
parentItem = mOptTreeModel->itemFromIndex( parentIndex );
}
else
{
QStandardItem *newParentItem = new QStandardItem( thisPath );
newParentItem->setToolTip( thisPath );
newParentItem->setSelectable( false );
if ( sourceIndex.isValid() )
{
// insert in model before sourceIndex
if ( parentItem )
parentItem->insertRow( sourceIndex.row(), newParentItem );
else
mOptTreeModel->insertRow( sourceIndex.row(), newParentItem );
}
else
{
// append to end
if ( parentItem )
parentItem->appendRow( newParentItem );
else
mOptTreeModel->appendRow( newParentItem );
}
parentItem = newParentItem;
}
}
QStandardItem *item = new QStandardItem( icon, title );
item->setToolTip( tooltip );
mOptTreeModel->insertRow( sourceIndexBefore.row(), item );
if ( parentItem )
{
if ( sourceBeforeIndices.empty() )
parentItem->appendRow( item );
else
{
parentItem->insertRow( sourceBeforeIndices.at( 0 ).row(), item );
}
}
else
{
mOptTreeModel->insertRow( sourceIndexBefore.row(), item );
}
}
mOptStackedWidget->insertWidget( idx, widget );
mOptStackedWidget->insertWidget( page, widget );
return;
}
}
// no matching pages, so just add the page
addPage( title, tooltip, icon, widget );
addPage( title, tooltip, icon, widget, path );
}
void QgsOptionsDialogBase::searchText( const QString &text )

View File

@ -149,10 +149,13 @@ class GUI_EXPORT QgsOptionsDialogBase : public QDialog
*
* The page content is specified via the \a widget argument. Ownership of \a widget is transferred to the dialog.
*
* Since QGIS 3.22, the optional \a path argument can be used to set the path of the item's entry in the tree view
* (for dialogs which show a tree view of options pages only).
*
* \see insertPage()
* \since QGIS 3.14
*/
void addPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget SIP_TRANSFER );
void addPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget SIP_TRANSFER, const QStringList &path = QStringList() );
/**
* Inserts a new page into the dialog pages.
@ -164,10 +167,13 @@ class GUI_EXPORT QgsOptionsDialogBase : public QDialog
* The \a before argument specifies the object name of an existing page. The new page will be inserted directly
* before the matching page.
*
* Since QGIS 3.22, the optional \a path argument can be used to set the path of the item's entry in the tree view
* (for dialogs which show a tree view of options pages only).
*
* \see addPage()
* \since QGIS 3.14
*/
void insertPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget SIP_TRANSFER, const QString &before );
void insertPage( const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget SIP_TRANSFER, const QString &before, const QStringList &path = QStringList() );
public slots:

View File

@ -148,6 +148,17 @@ class GUI_EXPORT QgsOptionsWidgetFactory : public QObject
*/
virtual QString pagePositionHint() const { return QString(); }
/**
* Returns the path to place the widget page at, for options dialogs
* which are structured using a tree view.
*
* A factory which returns "Code", "Javascript" would have its widget placed
* in a group named "Javascript", contained in a parent group named "Code".
*
* \since QGIS 3.22
*/
virtual QStringList path() const { return QStringList(); }
/**
* \brief Factory function to create the widget on demand as needed by the options dialog.
* \param parent The parent of the widget.