Allow options dialog to use a tree structure for tab list instead

of a flat list widget
This commit is contained in:
Nyall Dawson 2021-06-22 14:46:22 +10:00
parent bb119aa979
commit f22f742a9f
8 changed files with 318 additions and 295 deletions

View File

@ -13,7 +13,6 @@
class QgsOptionsDialogBase : QDialog
{
%Docstring(signature="appended")
@ -168,6 +167,8 @@ it is automatically called if a line edit has "mSearchLineEdit" as object name.
%End
};
/************************************************************************

View File

@ -104,6 +104,33 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
{
setupUi( this );
mTreeModel = new QStandardItemModel( this );
mTreeModel->appendRow( createItem( tr( "General" ), tr( "General" ), QStringLiteral( "propertyicons/general.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "System" ), tr( "System" ), QStringLiteral( "propertyicons/system.svg" ) ) );
QStandardItem *crsGroup = new QStandardItem( 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" ) ) );
mTreeModel->appendRow( crsGroup );
mTreeModel->appendRow( createItem( tr( "Data Sources" ), tr( "Data sources" ), QStringLiteral( "propertyicons/attributes.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Rendering" ), tr( "Rendering" ), QStringLiteral( "propertyicons/rendering.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Canvas & Legend" ), tr( "Canvas and legend" ), QStringLiteral( "propertyicons/overlay.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Map Tools" ), tr( "Map tools" ), QStringLiteral( "propertyicons/map_tools.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Colors" ), tr( "Colors" ), QStringLiteral( "propertyicons/colors.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Digitizing" ), tr( "Digitizing" ), QStringLiteral( "propertyicons/digitizing.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Layouts" ), tr( "Print layouts" ), QStringLiteral( "mIconLayout.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "GDAL" ), tr( "GDAL" ), QStringLiteral( "propertyicons/gdal.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Variables" ), tr( "Variables" ), QStringLiteral( "mIconExpression.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Authentication" ), tr( "Authentication" ), QStringLiteral( "locked.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Network" ), tr( "Network" ), QStringLiteral( "propertyicons/network_and_proxy.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Locator" ), tr( "Locator" ), QStringLiteral( "search.svg" ) ) );
mTreeModel->appendRow( createItem( tr( "Acceleration" ), tr( "GPU acceleration" ), QStringLiteral( "mIconGPU.svg" ) ) );
mOptionsTreeView->setModel( mTreeModel );
connect( cbxProjectDefaultNew, &QCheckBox::toggled, this, &QgsOptions::cbxProjectDefaultNew_toggled );
connect( leLayerGlobalCrs, &QgsProjectionSelectionWidget::crsChanged, this, &QgsOptions::leLayerGlobalCrs_crsChanged );
connect( lstRasterDrivers, &QTreeWidget::itemDoubleClicked, this, &QgsOptions::lstRasterDrivers_itemDoubleClicked );
@ -1334,7 +1361,10 @@ void QgsOptions::checkPageWidgetNameMap()
{
const QMap< QString, int > pageNames = QgisApp::instance()->optionsPagesMap();
#if 0
Q_ASSERT_X( pageNames.count() == mOptionsListWidget->count(), "QgsOptions::checkPageWidgetNameMap()", "QgisApp::optionsPagesMap() is outdated, contains too many entries" );
for ( int idx = 0; idx < mOptionsListWidget->count(); ++idx )
{
QWidget *currentPage = mOptionsStackedWidget->widget( idx );
@ -1346,6 +1376,7 @@ void QgsOptions::checkPageWidgetNameMap()
Q_ASSERT_X( pageNames.value( title ) == idx, "QgsOptions::checkPageWidgetNameMap()", QStringLiteral( "QgisApp::optionsPagesMap() is outdated, please update. %1 should be %2 not %3" ).arg( title ).arg( idx ).arg( pageNames.value( title ) ).toLocal8Bit().constData() );
}
}
#endif
}
void QgsOptions::setCurrentPage( const QString &pageWidgetName )

View File

@ -34,6 +34,7 @@ class QgsOptionsPageWidget;
class QgsLocatorOptionsWidget;
class QgsAuthConfigSelect;
class QgsBearingNumericFormat;
class QStandardItemModel;
/**
* \class QgsOptions
@ -313,6 +314,8 @@ class APP_EXPORT QgsOptions : public QgsOptionsDialogBase, private Ui::QgsOption
std::unique_ptr< QgsBearingNumericFormat > mBearingFormat;
QStandardItemModel *mTreeModel = nullptr;
void updateActionsForCurrentColorScheme( QgsColorScheme *scheme );
void checkPageWidgetNameMap();

View File

@ -152,6 +152,23 @@ void QgisAppStyleSheet::buildStyleSheet( const QMap<QString, QVariant> &opts )
" padding-right: 0px;"
"}" ).arg( frameMargin );
style += QStringLiteral( "QTreeView#mOptionsTreeView {"
" background-color: rgba(69, 69, 69, 0);"
" outline: 0;"
"}"
"QFrame#mOptionsListFrame {"
" background-color: rgba(69, 69, 69, 220);"
"}"
"QTreeView#mOptionsTreeView::item {"
" color: white;"
" padding: %1px;"
"}"
"QTreeView#mOptionsTreeView::item::selected {"
" color: black;"
" background-color:palette(Window);"
" padding-right: 0px;"
"}" ).arg( frameMargin );
QString toolbarSpacing = opts.value( QStringLiteral( "toolbarSpacing" ), QString() ).toString();
if ( !toolbarSpacing.isEmpty() )
{

View File

@ -306,6 +306,7 @@ void QgsAppScreenShots::takeGlobalOptions()
dlg->setMinimumHeight( 600 );
dlg->show();
QCoreApplication::processEvents();
#if 0
for ( int row = 0; row < dlg->mOptionsListWidget->count(); ++row )
{
dlg->mOptionsListWidget->setCurrentRow( row );
@ -327,7 +328,7 @@ void QgsAppScreenShots::takeGlobalOptions()
QCoreApplication::processEvents();
QCoreApplication::processEvents(); // seems a second call is needed, the tabble might not be fully displayed otherwise
takeScreenshot( QStringLiteral( "advanced_with_settings_shown" ), folder, dlg );
#endif
// exit properly
dlg->close();
dlg->deleteLater();

View File

@ -27,6 +27,10 @@
#include <QSplitter>
#include <QStackedWidget>
#include <QTimer>
#include <QStandardItem>
#include <QTreeView>
#include <QHeaderView>
#include <functional>
#include "qgsfilterlineedit.h"
#include "qgsmessagebaritem.h"
@ -34,14 +38,12 @@
#include "qgsoptionsdialoghighlightwidget.h"
#include "qgsoptionswidgetfactory.h"
#include "qgsguiutils.h"
#include "qgsapplication.h"
QgsOptionsDialogBase::QgsOptionsDialogBase( const QString &settingsKey, QWidget *parent, Qt::WindowFlags fl, QgsSettings *settings )
: QDialog( parent, fl )
, mOptsKey( settingsKey )
, mInit( false )
, mIconOnly( false )
, mSettings( settings )
, mDelSettings( false )
{
}
@ -90,6 +92,12 @@ void QgsOptionsDialogBase::initOptionsBase( bool restoreUi, const QString &title
// start with copy of qgsoptionsdialog_template.ui to ensure existence of these objects
mOptListWidget = findChild<QListWidget *>( QStringLiteral( "mOptionsListWidget" ) );
mOptTreeView = findChild<QTreeView *>( QStringLiteral( "mOptionsTreeView" ) );
if ( mOptTreeView )
{
mOptTreeModel = qobject_cast< QStandardItemModel * >( mOptTreeView->model() );
}
QFrame *optionsFrame = findChild<QFrame *>( QStringLiteral( "mOptionsFrame" ) );
mOptStackedWidget = findChild<QStackedWidget *>( QStringLiteral( "mOptionsStackedWidget" ) );
mOptSplitter = findChild<QSplitter *>( QStringLiteral( "mOptionsSplitter" ) );
@ -97,17 +105,28 @@ void QgsOptionsDialogBase::initOptionsBase( bool restoreUi, const QString &title
QFrame *buttonBoxFrame = findChild<QFrame *>( QStringLiteral( "mButtonBoxFrame" ) );
mSearchLineEdit = findChild<QgsFilterLineEdit *>( QStringLiteral( "mSearchLineEdit" ) );
if ( !mOptListWidget || !mOptStackedWidget || !mOptSplitter || !optionsFrame )
if ( ( !mOptListWidget && !mOptTreeView ) || !mOptStackedWidget || !mOptSplitter || !optionsFrame )
{
return;
}
int size = QgsGuiUtils::scaleIconSize( mSettings->value( QStringLiteral( "/IconSize" ), 24 ).toInt() );
// buffer size to match displayed icon size in toolbars, and expected geometry restore
// newWidth (above) may need adjusted if you adjust iconBuffer here
const int iconBuffer = QgsGuiUtils::scaleIconSize( 4 );
mOptListWidget->setIconSize( QSize( size + iconBuffer, size + iconBuffer ) );
mOptListWidget->setFrameStyle( QFrame::NoFrame );
QAbstractItemView *optView = mOptListWidget ? static_cast< QAbstractItemView * >( mOptListWidget ) : static_cast< QAbstractItemView * >( mOptTreeView );
int iconSize = 16;
if ( mOptListWidget )
{
int size = QgsGuiUtils::scaleIconSize( mSettings->value( QStringLiteral( "/IconSize" ), 24 ).toInt() );
// buffer size to match displayed icon size in toolbars, and expected geometry restore
// newWidth (above) may need adjusted if you adjust iconBuffer here
const int iconBuffer = QgsGuiUtils::scaleIconSize( 4 );
iconSize = size + iconBuffer;
}
else if ( mOptTreeView )
{
iconSize = QgsGuiUtils::scaleIconSize( mSettings->value( QStringLiteral( "/IconSize" ), 16 ).toInt() );
mOptTreeView->header()->setVisible( false );
}
optView->setIconSize( QSize( iconSize, iconSize ) );
optView->setFrameStyle( QFrame::NoFrame );
const int frameMargin = QgsGuiUtils::scaleIconSize( 3 );
optionsFrame->layout()->setContentsMargins( 0, frameMargin, frameMargin, frameMargin );
@ -135,6 +154,24 @@ void QgsOptionsDialogBase::initOptionsBase( bool restoreUi, const QString &title
connect( mOptStackedWidget, &QStackedWidget::currentChanged, this, &QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged );
connect( mOptStackedWidget, &QStackedWidget::widgetRemoved, this, &QgsOptionsDialogBase::optionsStackedWidget_WidgetRemoved );
if ( mOptTreeView )
{
// sync selection in tree view with current stacked widget index
connect( mOptTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, mOptStackedWidget, [ = ]( const QItemSelection &, const QItemSelection & )
{
const QModelIndexList selected = mOptTreeView->selectionModel()->selectedIndexes();
if ( selected.isEmpty() )
return;
const QModelIndex index = selected.at( 0 );
if ( !mOptTreeModel || !mOptTreeModel->itemFromIndex( index )->isSelectable() )
return;
mOptStackedWidget->setCurrentIndex( viewIndexToPageNumber( index ) );
} );
}
if ( mSearchLineEdit )
{
mSearchLineEdit->setShowSearchIcon( true );
@ -179,15 +216,19 @@ void QgsOptionsDialogBase::restoreOptionsBaseUi( const QString &title )
restoreGeometry( mSettings->value( QStringLiteral( "/Windows/%1/geometry" ).arg( mOptsKey ) ).toByteArray() );
// mOptListWidget width is fixed to take up less space in QtDesigner
// revert it now unless the splitter's state hasn't been saved yet
mOptListWidget->setMaximumWidth(
mSettings->value( QStringLiteral( "/Windows/%1/splitState" ).arg( mOptsKey ) ).isNull() ? 150 : 16777215 );
QAbstractItemView *optView = mOptListWidget ? static_cast< QAbstractItemView * >( mOptListWidget ) : static_cast< QAbstractItemView * >( mOptTreeView );
if ( optView )
{
optView->setMaximumWidth(
mSettings->value( QStringLiteral( "/Windows/%1/splitState" ).arg( mOptsKey ) ).isNull() ? 150 : 16777215 );
// get rid of annoying outer focus rect on Mac
optView->setAttribute( Qt::WA_MacShowFocusRect, false );
}
mOptSplitter->restoreState( mSettings->value( QStringLiteral( "/Windows/%1/splitState" ).arg( mOptsKey ) ).toByteArray() );
restoreLastPage();
// get rid of annoying outer focus rect on Mac
mOptListWidget->setAttribute( Qt::WA_MacShowFocusRect, false );
// brute force approach to try to standardize page margins!
for ( int i = 0; i < mOptStackedWidget->count(); ++i )
{
@ -217,11 +258,84 @@ void QgsOptionsDialogBase::restoreLastPage()
}
}
if ( mOptStackedWidget->count() != 0 && mOptListWidget->count() != 0 )
if ( mOptStackedWidget->count() == 0 )
return;
mOptStackedWidget->setCurrentIndex( curIndx );
setListToItemAtIndex( curIndx );
}
void QgsOptionsDialogBase::setListToItemAtIndex( int index )
{
if ( mOptListWidget && mOptListWidget->count() > index )
{
mOptStackedWidget->setCurrentIndex( curIndx );
mOptListWidget->setCurrentRow( curIndx );
mOptListWidget->setCurrentRow( index );
}
else if ( mOptTreeView && mOptTreeModel )
{
mOptTreeView->setCurrentIndex( pageNumberToTreeViewIndex( index ) );
}
}
QModelIndex QgsOptionsDialogBase::pageNumberToTreeViewIndex( int page )
{
if ( !mOptTreeModel )
return QModelIndex();
int pagesRemaining = page;
std::function<QModelIndex( const QModelIndex & )> traversePages;
// traverse through the model, counting all selectable items until we hit the desired page number
traversePages = [&]( const QModelIndex & parent ) -> QModelIndex
{
for ( int row = 0; row < mOptTreeModel->rowCount( parent ); ++row )
{
const QModelIndex currentIndex = mOptTreeModel->index( row, 0, parent );
if ( mOptTreeModel->itemFromIndex( currentIndex )->isSelectable() && pagesRemaining == 0 )
return currentIndex;
const QModelIndex res = traversePages( currentIndex );
if ( res.isValid() )
return res;
if ( mOptTreeModel->itemFromIndex( currentIndex )->isSelectable() )
pagesRemaining--;
}
return QModelIndex();
};
return traversePages( QModelIndex() );
}
int QgsOptionsDialogBase::viewIndexToPageNumber( const QModelIndex &index )
{
if ( !mOptTreeModel )
return 0;
int page = 0;
std::function<int( const QModelIndex & )> traverseModel;
// traverse through the model, counting all which correspond to pages till we hit the desired index
traverseModel = [&]( const QModelIndex & parent ) -> int
{
for ( int row = 0; row < mOptTreeModel->rowCount( parent ); ++row )
{
const QModelIndex currentIndex = mOptTreeModel->index( row, 0, parent );
if ( currentIndex == index )
return page;
const int res = traverseModel( currentIndex );
if ( res >= 0 )
return res;
if ( mOptTreeModel->itemFromIndex( currentIndex )->isSelectable() )
page++;
}
return -1;
};
return traverseModel( QModelIndex() );
}
void QgsOptionsDialogBase::resizeAlltabs( int index )
@ -270,7 +384,17 @@ void QgsOptionsDialogBase::addPage( const QString &title, const QString &tooltip
item->setText( title );
item->setToolTip( tooltip );
mOptListWidget->addItem( item );
if ( mOptListWidget )
{
mOptListWidget->addItem( item );
}
else if ( mOptTreeModel )
{
QStandardItem *item = new QStandardItem( icon, title );
item->setToolTip( tooltip );
mOptTreeModel->appendRow( item );
}
mOptStackedWidget->addWidget( widget );
}
@ -289,7 +413,17 @@ void QgsOptionsDialogBase::insertPage( const QString &title, const QString &tool
item->setText( title );
item->setToolTip( tooltip );
mOptListWidget->insertItem( idx, item );
if ( mOptListWidget )
{
mOptListWidget->insertItem( idx, item );
}
else if ( mOptTreeModel )
{
QStandardItem *item = new QStandardItem( icon, title );
item->setToolTip( tooltip );
mOptTreeModel->insertRow( idx, item );
}
mOptStackedWidget->insertWidget( idx, widget );
return;
}
@ -312,21 +446,28 @@ void QgsOptionsDialogBase::searchText( const QString &text )
mOptStackedWidget->show();
if ( mOptButtonBox && mOptButtonBox->isHidden() )
mOptButtonBox->show();
// hide all page if text has to be search, show them all otherwise
for ( int r = 0; r < mOptListWidget->count(); ++r )
if ( mOptListWidget )
{
mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
for ( int r = 0; r < mOptListWidget->count(); ++r )
{
mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
}
}
for ( const QPair< QgsOptionsDialogHighlightWidget *, int > &rsw : std::as_const( mRegisteredSearchWidgets ) )
{
if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
{
mOptListWidget->setRowHidden( rsw.second, false );
if ( mOptListWidget )
{
mOptListWidget->setRowHidden( rsw.second, false );
}
}
}
if ( mOptListWidget->isRowHidden( mOptStackedWidget->currentIndex() ) )
if ( mOptListWidget && mOptListWidget->isRowHidden( mOptStackedWidget->currentIndex() ) )
{
for ( int r = 0; r < mOptListWidget->count(); ++r )
{
@ -385,16 +526,30 @@ void QgsOptionsDialogBase::registerTextSearchWidgets()
}
}
QStandardItem *QgsOptionsDialogBase::createItem( const QString &name, const QString &tooltip, const QString &icon )
{
QStandardItem *res = new QStandardItem( QgsApplication::getThemeIcon( icon ), name );
res->setToolTip( tooltip );
return res;
}
void QgsOptionsDialogBase::showEvent( QShowEvent *e )
{
if ( mInit )
{
updateOptionsListVerticalTabs();
optionsStackedWidget_CurrentChanged( mOptListWidget->currentRow() );
if ( mOptListWidget )
{
optionsStackedWidget_CurrentChanged( mOptListWidget->currentRow() );
}
else if ( mOptTreeView )
{
optionsStackedWidget_CurrentChanged( viewIndexToPageNumber( mOptTreeView->currentIndex() ) );
}
}
else
{
QTimer::singleShot( 0, this, SLOT( warnAboutMissingObjects() ) );
QTimer::singleShot( 0, this, &QgsOptionsDialogBase::warnAboutMissingObjects );
}
if ( mSearchLineEdit )
@ -408,20 +563,21 @@ void QgsOptionsDialogBase::showEvent( QShowEvent *e )
void QgsOptionsDialogBase::paintEvent( QPaintEvent *e )
{
if ( mInit )
QTimer::singleShot( 0, this, SLOT( updateOptionsListVerticalTabs() ) );
QTimer::singleShot( 0, this, &QgsOptionsDialogBase::updateOptionsListVerticalTabs );
QDialog::paintEvent( e );
}
void QgsOptionsDialogBase::updateWindowTitle()
{
QListWidgetItem *curitem = mOptListWidget->currentItem();
if ( curitem )
const QString itemText = mOptListWidget && mOptListWidget->currentItem() ? mOptListWidget->currentItem()->text()
: mOptTreeView && mOptTreeView->currentIndex().isValid() ? mOptTreeView->currentIndex().data( Qt::DisplayRole ).toString() : QString();
if ( !itemText.isEmpty() )
{
setWindowTitle( QStringLiteral( "%1 %2 %3" )
.arg( mDialogTitle )
.arg( QChar( 0x2014 ) ) // em-dash unicode
.arg( curitem->text() ) );
.arg( itemText ) );
}
else
{
@ -434,42 +590,58 @@ void QgsOptionsDialogBase::updateOptionsListVerticalTabs()
if ( !mInit )
return;
if ( mOptListWidget->maximumWidth() != 16777215 )
mOptListWidget->setMaximumWidth( 16777215 );
// auto-resize splitter for vert scrollbar without covering icons in icon-only mode
// TODO: mOptListWidget has fixed 32px wide icons for now, allow user-defined
// Note: called on splitter resize and dialog paint event, so only update when necessary
int iconWidth = mOptListWidget->iconSize().width();
int snapToIconWidth = iconWidth + 32;
QList<int> splitSizes = mOptSplitter->sizes();
mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
// iconBuffer (above) may need adjusted if you adjust iconWidth here
int newWidth = mOptListWidget->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
bool diffWidth = mOptListWidget->minimumWidth() != newWidth;
if ( diffWidth )
mOptListWidget->setMinimumWidth( newWidth );
if ( mIconOnly && ( diffWidth || mOptListWidget->width() != newWidth ) )
QAbstractItemView *optView = mOptListWidget ? static_cast< QAbstractItemView * >( mOptListWidget ) : static_cast< QAbstractItemView * >( mOptTreeView );
if ( optView )
{
splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
splitSizes[0] = newWidth;
mOptSplitter->setSizes( splitSizes );
}
if ( optView->maximumWidth() != 16777215 )
optView->setMaximumWidth( 16777215 );
// auto-resize splitter for vert scrollbar without covering icons in icon-only mode
// TODO: mOptListWidget has fixed 32px wide icons for now, allow user-defined
// Note: called on splitter resize and dialog paint event, so only update when necessary
int iconWidth = optView->iconSize().width();
int snapToIconWidth = iconWidth + 32;
if ( mOptListWidget->wordWrap() && mIconOnly )
mOptListWidget->setWordWrap( false );
if ( !mOptListWidget->wordWrap() && !mIconOnly )
mOptListWidget->setWordWrap( true );
QList<int> splitSizes = mOptSplitter->sizes();
mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
// iconBuffer (above) may need adjusted if you adjust iconWidth here
int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
bool diffWidth = optView->minimumWidth() != newWidth;
if ( diffWidth )
optView->setMinimumWidth( newWidth );
if ( mIconOnly && ( diffWidth || optView->width() != newWidth ) )
{
splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
splitSizes[0] = newWidth;
mOptSplitter->setSizes( splitSizes );
}
if ( mOptListWidget )
{
if ( mOptListWidget->wordWrap() && mIconOnly )
mOptListWidget->setWordWrap( false );
if ( !mOptListWidget->wordWrap() && !mIconOnly )
mOptListWidget->setWordWrap( true );
}
}
}
void QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( int index )
{
mOptListWidget->blockSignals( true );
mOptListWidget->setCurrentRow( index );
mOptListWidget->blockSignals( false );
if ( mOptListWidget )
{
mOptListWidget->blockSignals( true );
mOptListWidget->setCurrentRow( index );
mOptListWidget->blockSignals( false );
}
else if ( mOptTreeView )
{
mOptTreeView->blockSignals( true );
mOptTreeView->setCurrentIndex( pageNumberToTreeViewIndex( index ) );
mOptTreeView->blockSignals( false );
}
updateWindowTitle();
}
@ -477,7 +649,14 @@ void QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( int index )
void QgsOptionsDialogBase::optionsStackedWidget_WidgetRemoved( int index )
{
// will need to take item first, if widgets are set for item in future
delete mOptListWidget->item( index );
if ( mOptListWidget )
{
delete mOptListWidget->item( index );
}
else if ( mOptTreeModel )
{
mOptTreeModel->removeRow( index );
}
QList<QPair< QgsOptionsDialogHighlightWidget *, int > >::iterator it = mRegisteredSearchWidgets.begin();
while ( it != mRegisteredSearchWidgets.end() )

View File

@ -35,11 +35,13 @@ class QPainter;
class QStackedWidget;
class QStyleOptionViewItem;
class QSplitter;
class QStandardItem;
class QTreeView;
class QStandardItemModel;
class QgsFilterLineEdit;
class QgsOptionsDialogHighlightWidget;
/**
* \ingroup gui
* \class QgsOptionsDialogBase
@ -177,21 +179,36 @@ class GUI_EXPORT QgsOptionsDialogBase : public QDialog
*/
void registerTextSearchWidgets();
/**
* Creates a new QStandardItem with the specified name, tooltip and icon.
*
* \since QGIS 3.22
*/
QStandardItem *createItem( const QString &name, const QString &tooltip, const QString &icon ) SIP_SKIP;
QList< QPair< QgsOptionsDialogHighlightWidget *, int > > mRegisteredSearchWidgets;
QString mOptsKey;
bool mInit;
bool mInit = false;
QListWidget *mOptListWidget = nullptr;
QTreeView *mOptTreeView = nullptr;
QStandardItemModel *mOptTreeModel = nullptr;
QStackedWidget *mOptStackedWidget = nullptr;
QSplitter *mOptSplitter = nullptr;
QDialogButtonBox *mOptButtonBox = nullptr;
QgsFilterLineEdit *mSearchLineEdit = nullptr;
QString mDialogTitle;
bool mIconOnly;
bool mIconOnly = false;
// pointer to app or custom, external QgsSettings
// QPointer in case custom settings obj gets deleted while dialog is open
QPointer<QgsSettings> mSettings;
bool mDelSettings;
bool mDelSettings = false;
private:
void setListToItemAtIndex( int index );
QModelIndex pageNumberToTreeViewIndex( int page );
int viewIndexToPageNumber( const QModelIndex &index );
};
#endif // QGSOPTIONSDIALOGBASE_H

View File

@ -61,7 +61,7 @@
<widget class="QgsFilterLineEdit" name="mSearchLineEdit"/>
</item>
<item>
<widget class="QListWidget" name="mOptionsListWidget">
<widget class="QTreeView" name="mOptionsTreeView">
<property name="minimumSize">
<size>
<width>58</width>
@ -89,216 +89,6 @@
<property name="textElideMode">
<enum>Qt::ElideNone</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>General</string>
</property>
<property name="toolTip">
<string>General</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/general.svg</normaloff>:/images/themes/default/propertyicons/general.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>System</string>
</property>
<property name="toolTip">
<string>System</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/system.svg</normaloff>:/images/themes/default/propertyicons/system.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>CRS</string>
</property>
<property name="toolTip">
<string>CRS</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/CRS.svg</normaloff>:/images/themes/default/propertyicons/CRS.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Transformations</string>
</property>
<property name="toolTip">
<string>Coordinate transformations and operations</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/transformation.svg</normaloff>:/images/themes/default/transformation.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Data Sources</string>
</property>
<property name="toolTip">
<string>Data sources</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/attributes.svg</normaloff>:/images/themes/default/propertyicons/attributes.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Rendering</string>
</property>
<property name="toolTip">
<string>Rendering</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/rendering.svg</normaloff>:/images/themes/default/propertyicons/rendering.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Canvas &amp; Legend</string>
</property>
<property name="toolTip">
<string>Canvas and legend</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/overlay.svg</normaloff>:/images/themes/default/propertyicons/overlay.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Map Tools</string>
</property>
<property name="toolTip">
<string>Map tools</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/map_tools.svg</normaloff>:/images/themes/default/propertyicons/map_tools.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Colors</string>
</property>
<property name="toolTip">
<string>Colors</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/colors.svg</normaloff>:/images/themes/default/propertyicons/colors.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Digitizing</string>
</property>
<property name="toolTip">
<string>Digitizing</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/digitizing.svg</normaloff>:/images/themes/default/propertyicons/digitizing.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Layouts</string>
</property>
<property name="toolTip">
<string>Print layouts</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconLayout.svg</normaloff>:/images/themes/default/mIconLayout.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>GDAL</string>
</property>
<property name="toolTip">
<string>GDAL</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/gdal.svg</normaloff>:/images/themes/default/propertyicons/gdal.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Variables</string>
</property>
<property name="toolTip">
<string>Variables</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconExpression.svg</normaloff>:/images/themes/default/mIconExpression.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Authentication</string>
</property>
<property name="toolTip">
<string>Authentication</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/locked.svg</normaloff>:/images/themes/default/locked.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Network</string>
</property>
<property name="toolTip">
<string>Network</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/network_and_proxy.svg</normaloff>:/images/themes/default/propertyicons/network_and_proxy.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Locator</string>
</property>
<property name="toolTip">
<string>Locator</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/search.svg</normaloff>:/images/themes/default/search.svg</iconset>
</property>
</item>
<item>
<property name="text">
<string>Acceleration</string>
</property>
<property name="toolTip">
<string>Configure GPU for processing algorithms</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconGPU.svg</normaloff>:/images/themes/default/mIconGPU.svg</iconset>
</property>
</item>
</widget>
</item>
</layout>
@ -6316,7 +6106,7 @@ p, li { white-space: pre-wrap; }
</customwidgets>
<tabstops>
<tabstop>mSearchLineEdit</tabstop>
<tabstop>mOptionsListWidget</tabstop>
<tabstop>mOptionsTreeView</tabstop>
<tabstop>mOptionsScrollArea_01</tabstop>
<tabstop>grpLocale</tabstop>
<tabstop>cboTranslation</tabstop>
@ -6550,22 +6340,6 @@ p, li { white-space: pre-wrap; }
<include location="../../images/images.qrc"/>
</resources>
<connections>
<connection>
<sender>mOptionsListWidget</sender>
<signal>currentRowChanged(int)</signal>
<receiver>mOptionsStackedWidget</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>144</x>
<y>196</y>
</hint>
<hint type="destinationlabel">
<x>790</x>
<y>43</y>
</hint>
</hints>
</connection>
<connection>
<sender>chkMaxThreads</sender>
<signal>toggled(bool)</signal>