31#include <QDialogButtonBox>
35#include <QListWidgetItem>
40#include <QStackedWidget>
41#include <QStandardItem>
46#include "moc_qgsoptionsdialogbase.cpp"
48using namespace Qt::StringLiterals;
51 : QDialog( parent, fl )
87 if ( title.isEmpty() )
94 if (
auto *lLayout = layout() )
96 lLayout->setContentsMargins( 0, 0, 0, 0 );
100 mOptListWidget = findChild<QListWidget *>( u
"mOptionsListWidget"_s );
101 mOptTreeView = findChild<QTreeView *>( u
"mOptionsTreeView"_s );
111 QFrame *optionsFrame = findChild<QFrame *>( u
"mOptionsFrame"_s );
113 mOptSplitter = findChild<QSplitter *>( u
"mOptionsSplitter"_s );
114 mOptButtonBox = findChild<QDialogButtonBox *>( u
"buttonBox"_s );
115 QFrame *buttonBoxFrame = findChild<QFrame *>( u
"mButtonBoxFrame"_s );
116 mSearchLineEdit = findChild<QgsFilterLineEdit *>( u
"mSearchLineEdit"_s );
131 iconSize = size + iconBuffer;
138 optView->setIconSize( QSize( iconSize, iconSize ) );
139 optView->setFrameStyle( QFrame::NoFrame );
142 optionsFrame->layout()->setContentsMargins( 0, frameMargin, frameMargin, frameMargin );
143 QVBoxLayout *layout =
static_cast<QVBoxLayout *
>( optionsFrame->layout() );
145 if ( buttonBoxFrame )
147 buttonBoxFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
148 layout->insertWidget( layout->count(), buttonBoxFrame );
158 disconnect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
159 connect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
160 disconnect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
161 connect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
170 connect(
mOptTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
mOptStackedWidget, [
this](
const QItemSelection &,
const QItemSelection & ) {
171 const QModelIndexList selected = mOptTreeView->selectionModel()->selectedIndexes();
172 if ( selected.isEmpty() )
175 const QModelIndex index = mTreeProxyModel->mapToSource( selected.at( 0 ) );
177 if ( !mOptTreeModel || !mOptTreeModel->itemFromIndex( index )->isSelectable() )
180 mOptStackedWidget->setCurrentIndex( mTreeProxyModel->sourceIndexToPageNumber( index ) );
218 if ( !title.isEmpty() )
229 restoreGeometry(
mSettings->value( u
"/Windows/%1/geometry"_s.arg(
mOptsKey ) ).toByteArray() );
237 optView->setAttribute( Qt::WA_MacShowFocusRect,
false );
249 l->setContentsMargins( 0, 0, 0, 0 );
256 int curIndx =
mSettings->value( u
"/Windows/%1/tab"_s.arg(
mOptsKey ), 0 ).toInt();
276 setListToItemAtIndex( curIndx );
279void QgsOptionsDialogBase::setListToItemAtIndex(
int index )
298 QSizePolicy::Policy policy = QSizePolicy::Ignored;
301 policy = QSizePolicy::MinimumExpanding;
321 if ( currentPage->objectName() == page )
330void QgsOptionsDialogBase::addPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QStringList &path,
const QString &key )
336 QListWidgetItem *item =
new QListWidgetItem();
337 item->setIcon( icon );
338 item->setText( title );
339 item->setToolTip( tooltip );
344 QStandardItem *item =
new QStandardItem( icon, title );
345 item->setToolTip( tooltip );
346 if ( !key.isEmpty() )
348 item->setData( key );
352 QStandardItem *parentItem =
nullptr;
355 QStringList parents = path;
356 while ( !parents.empty() )
358 const QString parentPath = parents.takeFirst();
360 QModelIndex thisParent;
361 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
363 const QModelIndex index =
mOptTreeModel->index( row, 0, parent );
364 if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0 || index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
372 if ( !thisParent.isValid() )
374 QStandardItem *newParentItem =
new QStandardItem( parentPath );
375 newParentItem->setToolTip( parentPath );
376 newParentItem->setSelectable(
false );
378 parentItem->appendRow( newParentItem );
381 parentItem = newParentItem;
393 parentItem->appendRow( item );
394 const QModelIndex newIndex =
mOptTreeModel->indexFromItem( item );
402 scrollArea->setWidgetResizable(
true );
403 scrollArea->setFrameShape( QFrame::NoFrame );
404 scrollArea->setObjectName( widget->objectName() );
405 scrollArea->setWidget( widget );
413void QgsOptionsDialogBase::insertPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QString &before,
const QStringList &path,
const QString &key )
419 if ( currentPage->objectName() == before )
425 QListWidgetItem *item =
new QListWidgetItem();
426 item->setIcon( icon );
427 item->setText( title );
428 item->setToolTip( tooltip );
433 QModelIndex sourceIndexBefore =
mTreeProxyModel->pageNumberToSourceIndex( page );
434 QList<QModelIndex> sourceBeforeIndices;
435 while ( sourceIndexBefore.parent().isValid() )
437 sourceBeforeIndices.insert( 0, sourceIndexBefore );
438 sourceIndexBefore = sourceIndexBefore.parent();
440 sourceBeforeIndices.insert( 0, sourceIndexBefore );
442 QStringList parentPaths = path;
444 QModelIndex parentIndex;
445 QStandardItem *parentItem =
nullptr;
446 while ( !parentPaths.empty() )
448 QString thisPath = parentPaths.takeFirst();
449 QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
451 if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0 || sourceIndex.data( Qt::UserRole + 1 ).toString().compare( thisPath, Qt::CaseInsensitive ) == 0 )
453 parentIndex = sourceIndex;
458 QStandardItem *newParentItem =
new QStandardItem( thisPath );
459 newParentItem->setToolTip( thisPath );
460 newParentItem->setSelectable(
false );
461 if ( sourceIndex.isValid() )
465 parentItem->insertRow( sourceIndex.row(), newParentItem );
467 mOptTreeModel->insertRow( sourceIndex.row(), newParentItem );
473 parentItem->appendRow( newParentItem );
477 parentItem = newParentItem;
481 QStandardItem *item =
new QStandardItem( icon, title );
482 item->setToolTip( tooltip );
483 if ( !key.isEmpty() )
485 item->setData( key );
489 if ( sourceBeforeIndices.empty() )
490 parentItem->appendRow( item );
493 parentItem->insertRow( sourceBeforeIndices.at( 0 ).row(), item );
503 scrollArea->setWidgetResizable(
true );
504 scrollArea->setFrameShape( QFrame::NoFrame );
505 scrollArea->setWidget( widget );
506 scrollArea->setObjectName( widget->objectName() );
513 addPage( title, tooltip, icon, widget, path );
518 const int minimumTextLength = 3;
535 if (
mOptListWidget->item( r )->text().contains( text, Qt::CaseInsensitive ) )
541 mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
547 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
555 QMap<int, bool> hiddenPages;
558 hiddenPages.insert( r, text.length() >= minimumTextLength );
561 std::function<void(
const QModelIndex & )> traverseModel;
563 traverseModel = [&](
const QModelIndex &parent ) {
564 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
566 const QModelIndex currentIndex =
mOptTreeModel->index( row, 0, parent );
567 if ( currentIndex.data().toString().contains( text, Qt::CaseInsensitive ) )
569 hiddenPages.insert(
mTreeProxyModel->sourceIndexToPageNumber( currentIndex ),
false );
571 traverseModel( currentIndex );
574 traverseModel( QModelIndex() );
578 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
580 hiddenPages.insert( rsw.second,
false );
583 for (
auto it = hiddenPages.constBegin(); it != hiddenPages.constEnd(); ++it )
588 if (
mOptTreeView && text.length() >= minimumTextLength )
614 if ( !
mTreeProxyModel->filterAcceptsRow( currentSourceIndex.row(), currentSourceIndex.parent() ) )
616 std::function<QModelIndex(
const QModelIndex & )> traverseModel;
617 traverseModel = [&](
const QModelIndex &parent ) -> QModelIndex {
620 const QModelIndex proxyIndex =
mTreeProxyModel->index( row, 0, parent );
621 const QModelIndex sourceIndex =
mTreeProxyModel->mapToSource( proxyIndex );
622 if (
mOptTreeModel->itemFromIndex( sourceIndex )->isSelectable() )
628 QModelIndex res = traverseModel( proxyIndex );
633 return QModelIndex();
636 const QModelIndex firstVisibleSourceIndex = traverseModel( QModelIndex() );
638 if ( firstVisibleSourceIndex.isValid() )
664 const QList<QWidget *> widgets =
mOptStackedWidget->widget( i )->findChildren<QWidget *>();
665 for ( QWidget *widget : widgets )
672 QHash<QWidget *, QgsOptionsDialogHighlightWidget *> customHighlightWidgets;
679 if ( customHighlightWidgets.contains( widget ) )
681 shw = customHighlightWidgets.value( widget );
705 res->setToolTip( tooltip );
733 QDialog::showEvent( e );
741 QDialog::paintEvent( e );
749 if ( !itemText.isEmpty() )
752 .arg( QChar( 0x2014 ) )
769 if ( optView->maximumWidth() != 16777215 )
770 optView->setMaximumWidth( 16777215 );
774 int iconWidth = optView->iconSize().width();
775 int snapToIconWidth = iconWidth + 32;
778 mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
781 int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
782 bool diffWidth = optView->minimumWidth() != newWidth;
785 optView->setMinimumWidth( newWidth );
787 if (
mIconOnly && ( diffWidth || optView->width() != newWidth ) )
789 splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
790 splitSizes[0] = newWidth;
837 if ( ( *it ).second == index )
846 QMessageBox::warning(
848 tr(
"Missing Objects" ),
850 "Base options dialog could not be initialized.\n\n"
851 "Missing some of the .ui template objects:\n"
852 ) +
" mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter,\n mOptionsListFrame",
860QgsOptionsProxyModel::QgsOptionsProxyModel( QObject *parent )
861 : QSortFilterProxyModel( parent )
863 setDynamicSortFilter(
true );
866void QgsOptionsProxyModel::setPageHidden(
int page,
bool hidden )
868 mHiddenPages[page] = hidden;
872QModelIndex QgsOptionsProxyModel::pageNumberToSourceIndex(
int page )
const
874 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
876 return QModelIndex();
878 int pagesRemaining = page;
879 std::function<QModelIndex(
const QModelIndex & )> traversePages;
882 traversePages = [&](
const QModelIndex &parent ) -> QModelIndex {
883 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
885 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
886 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
888 if ( pagesRemaining == 0 )
895 const QModelIndex res = traversePages( currentIndex );
899 return QModelIndex();
902 return traversePages( QModelIndex() );
905int QgsOptionsProxyModel::sourceIndexToPageNumber(
const QModelIndex &index )
const
907 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
913 std::function<int(
const QModelIndex & )> traverseModel;
916 traverseModel = [&](
const QModelIndex &parent ) ->
int {
917 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
919 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
920 if ( currentIndex == index )
923 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
926 const int res = traverseModel( currentIndex );
933 return traverseModel( QModelIndex() );
936bool QgsOptionsProxyModel::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent )
const
938 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
942 const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );
944 const int pageNumber = sourceIndexToPageNumber( sourceIndex );
945 if ( !mHiddenPages.value( pageNumber,
false ) )
948 if ( sourceModel()->hasChildren( sourceIndex ) )
951 for (
int row = 0; row < sourceModel()->rowCount( sourceIndex ); ++row )
953 if ( filterAcceptsRow( row, sourceIndex ) )
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
void cleared()
Emitted when the widget is cleared.
virtual void updateWindowTitle()
QPointer< QgsSettings > mSettings
void warnAboutMissingObjects()
void resizeAlltabs(int index)
Resizes all tabs when the dialog is resized.
void paintEvent(QPaintEvent *e) override
void restoreLastPage()
Refocus the active tab from the last time the dialog was shown.
QList< QPair< QgsOptionsDialogHighlightWidget *, int > > mRegisteredSearchWidgets
void searchText(const QString &text)
searchText searches for a text in all the pages of the stacked widget and highlight the results
void registerTextSearchWidgets()
register widgets in the dialog to search for text in it it is automatically called if a line edit has...
virtual void optionsStackedWidget_CurrentChanged(int index)
Select relevant tab on current page change.
~QgsOptionsDialogBase() override
QgsOptionsDialogBase(const QString &settingsKey, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags(), QgsSettings *settings=nullptr)
Constructor.
QgsFilterLineEdit * mSearchLineEdit
void setSettings(QgsSettings *settings)
virtual void optionsStackedWidget_WidgetRemoved(int index)
Remove tab and unregister widgets on page remove.
QDialogButtonBox * mOptButtonBox
QgsOptionsProxyModel * mTreeProxyModel
void addPage(const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QStringList &path=QStringList(), const QString &key=QString())
Adds a new page to the dialog pages.
QStandardItemModel * mOptTreeModel
QStandardItem * createItem(const QString &name, const QString &tooltip, const QString &icon)
Creates a new QStandardItem with the specified name, tooltip and icon.
virtual void updateOptionsListVerticalTabs()
Update tabs on the splitter move.
QListWidget * mOptListWidget
void restoreOptionsBaseUi(const QString &title=QString())
Restore the base ui.
QStackedWidget * mOptStackedWidget
void initOptionsBase(bool restoreUi=true, const QString &title=QString())
Set up the base ui connections for vertical tabs.
void showEvent(QShowEvent *e) override
void insertPage(const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QString &before, const QStringList &path=QStringList(), const QString &key=QString())
Inserts a new page into the dialog pages.
void setCurrentPage(const QString &page)
Sets the dialog page (by object name) to show.
Base class for widgets for pages included in the options dialog.
QHash< QWidget *, QgsOptionsDialogHighlightWidget * > registeredHighlightWidgets()
Returns the registered highlight widgets used to search and highlight text in options dialogs.
Stores settings for use within QGIS.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define QgsDebugMsgLevel(str, level)