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 )
88 if ( title.isEmpty() )
95 if (
auto *lLayout = layout() )
97 lLayout->setContentsMargins( 0, 0, 0, 0 );
101 mOptListWidget = findChild<QListWidget *>( u
"mOptionsListWidget"_s );
102 mOptTreeView = findChild<QTreeView *>( u
"mOptionsTreeView"_s );
112 QFrame *optionsFrame = findChild<QFrame *>( u
"mOptionsFrame"_s );
114 mOptSplitter = findChild<QSplitter *>( u
"mOptionsSplitter"_s );
115 mOptButtonBox = findChild<QDialogButtonBox *>( u
"buttonBox"_s );
116 QFrame *buttonBoxFrame = findChild<QFrame *>( u
"mButtonBoxFrame"_s );
117 mSearchLineEdit = findChild<QgsFilterLineEdit *>( u
"mSearchLineEdit"_s );
132 iconSize = size + iconBuffer;
139 optView->setIconSize( QSize( iconSize, iconSize ) );
140 optView->setFrameStyle( QFrame::NoFrame );
143 optionsFrame->layout()->setContentsMargins( 0, frameMargin, frameMargin, frameMargin );
144 QVBoxLayout *layout =
static_cast<QVBoxLayout *
>( optionsFrame->layout() );
146 if ( buttonBoxFrame )
148 buttonBoxFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
149 layout->insertWidget( layout->count(), buttonBoxFrame );
159 disconnect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
160 connect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
161 disconnect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
162 connect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
171 connect(
mOptTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
mOptStackedWidget, [
this](
const QItemSelection &,
const QItemSelection & ) {
172 const QModelIndexList selected = mOptTreeView->selectionModel()->selectedIndexes();
173 if ( selected.isEmpty() )
176 const QModelIndex index = mTreeProxyModel->mapToSource( selected.at( 0 ) );
178 if ( !mOptTreeModel || !mOptTreeModel->itemFromIndex( index )->isSelectable() )
181 mOptStackedWidget->setCurrentIndex( mTreeProxyModel->sourceIndexToPageNumber( index ) );
219 if ( !title.isEmpty() )
230 restoreGeometry(
mSettings->value( u
"/Windows/%1/geometry"_s.arg(
mOptsKey ) ).toByteArray() );
236 optView->setMaximumWidth(
240 optView->setAttribute( Qt::WA_MacShowFocusRect,
false );
252 l->setContentsMargins( 0, 0, 0, 0 );
259 int curIndx =
mSettings->value( u
"/Windows/%1/tab"_s.arg(
mOptsKey ), 0 ).toInt();
280 setListToItemAtIndex( curIndx );
283void QgsOptionsDialogBase::setListToItemAtIndex(
int index )
302 QSizePolicy::Policy policy = QSizePolicy::Ignored;
305 policy = QSizePolicy::MinimumExpanding;
325 if ( currentPage->objectName() == page )
334void QgsOptionsDialogBase::addPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QStringList &path,
const QString &key )
340 QListWidgetItem *item =
new QListWidgetItem();
341 item->setIcon( icon );
342 item->setText( title );
343 item->setToolTip( tooltip );
348 QStandardItem *item =
new QStandardItem( icon, title );
349 item->setToolTip( tooltip );
350 if ( !key.isEmpty() )
352 item->setData( key );
356 QStandardItem *parentItem =
nullptr;
359 QStringList parents = path;
360 while ( !parents.empty() )
362 const QString parentPath = parents.takeFirst();
364 QModelIndex thisParent;
365 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
367 const QModelIndex index =
mOptTreeModel->index( row, 0, parent );
368 if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0
369 || index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
377 if ( !thisParent.isValid() )
379 QStandardItem *newParentItem =
new QStandardItem( parentPath );
380 newParentItem->setToolTip( parentPath );
381 newParentItem->setSelectable(
false );
383 parentItem->appendRow( newParentItem );
386 parentItem = newParentItem;
398 parentItem->appendRow( item );
399 const QModelIndex newIndex =
mOptTreeModel->indexFromItem( item );
407 scrollArea->setWidgetResizable(
true );
408 scrollArea->setFrameShape( QFrame::NoFrame );
409 scrollArea->setObjectName( widget->objectName() );
410 scrollArea->setWidget( widget );
418void QgsOptionsDialogBase::insertPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QString &before,
const QStringList &path,
const QString &key )
424 if ( currentPage->objectName() == before )
430 QListWidgetItem *item =
new QListWidgetItem();
431 item->setIcon( icon );
432 item->setText( title );
433 item->setToolTip( tooltip );
438 QModelIndex sourceIndexBefore =
mTreeProxyModel->pageNumberToSourceIndex( page );
439 QList<QModelIndex> sourceBeforeIndices;
440 while ( sourceIndexBefore.parent().isValid() )
442 sourceBeforeIndices.insert( 0, sourceIndexBefore );
443 sourceIndexBefore = sourceIndexBefore.parent();
445 sourceBeforeIndices.insert( 0, sourceIndexBefore );
447 QStringList parentPaths = path;
449 QModelIndex parentIndex;
450 QStandardItem *parentItem =
nullptr;
451 while ( !parentPaths.empty() )
453 QString thisPath = parentPaths.takeFirst();
454 QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
456 if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0
457 || sourceIndex.data( Qt::UserRole + 1 ).toString().compare( thisPath, Qt::CaseInsensitive ) == 0 )
459 parentIndex = sourceIndex;
464 QStandardItem *newParentItem =
new QStandardItem( thisPath );
465 newParentItem->setToolTip( thisPath );
466 newParentItem->setSelectable(
false );
467 if ( sourceIndex.isValid() )
471 parentItem->insertRow( sourceIndex.row(), newParentItem );
473 mOptTreeModel->insertRow( sourceIndex.row(), newParentItem );
479 parentItem->appendRow( newParentItem );
483 parentItem = newParentItem;
487 QStandardItem *item =
new QStandardItem( icon, title );
488 item->setToolTip( tooltip );
489 if ( !key.isEmpty() )
491 item->setData( key );
495 if ( sourceBeforeIndices.empty() )
496 parentItem->appendRow( item );
499 parentItem->insertRow( sourceBeforeIndices.at( 0 ).row(), item );
509 scrollArea->setWidgetResizable(
true );
510 scrollArea->setFrameShape( QFrame::NoFrame );
511 scrollArea->setWidget( widget );
512 scrollArea->setObjectName( widget->objectName() );
519 addPage( title, tooltip, icon, widget, path );
524 const int minimumTextLength = 3;
541 if (
mOptListWidget->item( r )->text().contains( text, Qt::CaseInsensitive ) )
547 mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
553 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
561 QMap<int, bool> hiddenPages;
564 hiddenPages.insert( r, text.length() >= minimumTextLength );
567 std::function<void(
const QModelIndex & )> traverseModel;
569 traverseModel = [&](
const QModelIndex &parent ) {
570 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
572 const QModelIndex currentIndex =
mOptTreeModel->index( row, 0, parent );
573 if ( currentIndex.data().toString().contains( text, Qt::CaseInsensitive ) )
575 hiddenPages.insert(
mTreeProxyModel->sourceIndexToPageNumber( currentIndex ),
false );
577 traverseModel( currentIndex );
580 traverseModel( QModelIndex() );
584 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
586 hiddenPages.insert( rsw.second,
false );
589 for (
auto it = hiddenPages.constBegin(); it != hiddenPages.constEnd(); ++it )
594 if (
mOptTreeView && text.length() >= minimumTextLength )
620 if ( !
mTreeProxyModel->filterAcceptsRow( currentSourceIndex.row(), currentSourceIndex.parent() ) )
622 std::function<QModelIndex(
const QModelIndex & )> traverseModel;
623 traverseModel = [&](
const QModelIndex &parent ) -> QModelIndex {
626 const QModelIndex proxyIndex =
mTreeProxyModel->index( row, 0, parent );
627 const QModelIndex sourceIndex =
mTreeProxyModel->mapToSource( proxyIndex );
628 if (
mOptTreeModel->itemFromIndex( sourceIndex )->isSelectable() )
634 QModelIndex res = traverseModel( proxyIndex );
639 return QModelIndex();
642 const QModelIndex firstVisibleSourceIndex = traverseModel( QModelIndex() );
644 if ( firstVisibleSourceIndex.isValid() )
670 const QList<QWidget *> widgets =
mOptStackedWidget->widget( i )->findChildren<QWidget *>();
671 for ( QWidget *widget : widgets )
678 QHash<QWidget *, QgsOptionsDialogHighlightWidget *> customHighlightWidgets;
685 if ( customHighlightWidgets.contains( widget ) )
687 shw = customHighlightWidgets.value( widget );
711 res->setToolTip( tooltip );
739 QDialog::showEvent( e );
747 QDialog::paintEvent( e );
755 if ( !itemText.isEmpty() )
757 setWindowTitle( u
"%1 %2 %3"_s
759 .arg( QChar( 0x2014 ) )
776 if ( optView->maximumWidth() != 16777215 )
777 optView->setMaximumWidth( 16777215 );
781 int iconWidth = optView->iconSize().width();
782 int snapToIconWidth = iconWidth + 32;
785 mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
788 int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
789 bool diffWidth = optView->minimumWidth() != newWidth;
792 optView->setMinimumWidth( newWidth );
794 if (
mIconOnly && ( diffWidth || optView->width() != newWidth ) )
796 splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
797 splitSizes[0] = newWidth;
844 if ( ( *it ).second == index )
853 QMessageBox::warning(
nullptr, tr(
"Missing Objects" ), tr(
"Base options dialog could not be initialized.\n\n"
854 "Missing some of the .ui template objects:\n" )
855 +
" mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter,\n mOptionsListFrame",
856 QMessageBox::Ok, QMessageBox::Ok );
861QgsOptionsProxyModel::QgsOptionsProxyModel( QObject *parent )
862 : QSortFilterProxyModel( parent )
864 setDynamicSortFilter(
true );
867void QgsOptionsProxyModel::setPageHidden(
int page,
bool hidden )
869 mHiddenPages[page] = hidden;
873QModelIndex QgsOptionsProxyModel::pageNumberToSourceIndex(
int page )
const
875 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
877 return QModelIndex();
879 int pagesRemaining = page;
880 std::function<QModelIndex(
const QModelIndex & )> traversePages;
883 traversePages = [&](
const QModelIndex &parent ) -> QModelIndex {
884 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
886 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
887 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
889 if ( pagesRemaining == 0 )
896 const QModelIndex res = traversePages( currentIndex );
900 return QModelIndex();
903 return traversePages( QModelIndex() );
906int QgsOptionsProxyModel::sourceIndexToPageNumber(
const QModelIndex &index )
const
908 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
914 std::function<int(
const QModelIndex & )> traverseModel;
917 traverseModel = [&](
const QModelIndex &parent ) ->
int {
918 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
920 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
921 if ( currentIndex == index )
924 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
927 const int res = traverseModel( currentIndex );
934 return traverseModel( QModelIndex() );
937bool QgsOptionsProxyModel::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent )
const
939 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
943 const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );
945 const int pageNumber = sourceIndexToPageNumber( sourceIndex );
946 if ( !mHiddenPages.value( pageNumber,
false ) )
949 if ( sourceModel()->hasChildren( sourceIndex ) )
952 for (
int row = 0; row < sourceModel()->rowCount( sourceIndex ); ++row )
954 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)