20#include <QDialogButtonBox>
23#include <QListWidgetItem>
28#include <QStackedWidget>
30#include <QStandardItem>
45 : QDialog( parent, fl )
46 , mOptsKey( settingsKey )
47 , mSettings( settings )
55 mSettings->setValue( QStringLiteral(
"/Windows/%1/geometry" ).arg(
mOptsKey ), saveGeometry() );
82 if ( title.isEmpty() )
89 if (
auto *lLayout = layout() )
91 lLayout->setContentsMargins( 0, 0, 0, 0 );
95 mOptListWidget = findChild<QListWidget *>( QStringLiteral(
"mOptionsListWidget" ) );
96 mOptTreeView = findChild<QTreeView *>( QStringLiteral(
"mOptionsTreeView" ) );
106 QFrame *optionsFrame = findChild<QFrame *>( QStringLiteral(
"mOptionsFrame" ) );
107 mOptStackedWidget = findChild<QStackedWidget *>( QStringLiteral(
"mOptionsStackedWidget" ) );
108 mOptSplitter = findChild<QSplitter *>( QStringLiteral(
"mOptionsSplitter" ) );
109 mOptButtonBox = findChild<QDialogButtonBox *>( QStringLiteral(
"buttonBox" ) );
110 QFrame *buttonBoxFrame = findChild<QFrame *>( QStringLiteral(
"mButtonBoxFrame" ) );
111 mSearchLineEdit = findChild<QgsFilterLineEdit *>( QStringLiteral(
"mSearchLineEdit" ) );
126 iconSize = size + iconBuffer;
133 optView->setIconSize( QSize( iconSize, iconSize ) );
134 optView->setFrameStyle( QFrame::NoFrame );
137 optionsFrame->layout()->setContentsMargins( 0, frameMargin, frameMargin, frameMargin );
138 QVBoxLayout *layout =
static_cast<QVBoxLayout *
>( optionsFrame->layout() );
140 if ( buttonBoxFrame )
142 buttonBoxFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
143 layout->insertWidget( layout->count(), buttonBoxFrame );
153 disconnect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
154 connect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
155 disconnect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
156 connect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
165 connect(
mOptTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
mOptStackedWidget, [ = ](
const QItemSelection &,
const QItemSelection & )
167 const QModelIndexList selected = mOptTreeView->selectionModel()->selectedIndexes();
168 if ( selected.isEmpty() )
171 const QModelIndex index = mTreeProxyModel->mapToSource( selected.at( 0 ) );
173 if ( !mOptTreeModel || !mOptTreeModel->itemFromIndex( index )->isSelectable() )
176 mOptStackedWidget->setCurrentIndex( mTreeProxyModel->sourceIndexToPageNumber( index ) );
214 if ( !title.isEmpty() )
225 restoreGeometry(
mSettings->value( QStringLiteral(
"/Windows/%1/geometry" ).arg(
mOptsKey ) ).toByteArray() );
231 optView->setMaximumWidth(
234 optView->setAttribute( Qt::WA_MacShowFocusRect,
false );
246 l->setContentsMargins( 0, 0, 0, 0 );
253 int curIndx =
mSettings->value( QStringLiteral(
"/Windows/%1/tab" ).arg(
mOptsKey ), 0 ).toInt();
274 setListToItemAtIndex( curIndx );
277void QgsOptionsDialogBase::setListToItemAtIndex(
int index )
296 QSizePolicy::Policy policy = QSizePolicy::Ignored;
299 policy = QSizePolicy::MinimumExpanding;
319 if ( currentPage->objectName() == page )
328void QgsOptionsDialogBase::addPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QStringList &path,
const QString &key )
334 QListWidgetItem *item =
new QListWidgetItem();
335 item->setIcon( icon );
336 item->setText( title );
337 item->setToolTip( tooltip );
342 QStandardItem *item =
new QStandardItem( icon, title );
343 item->setToolTip( tooltip );
344 if ( !key.isEmpty() )
346 item->setData( key );
350 QStandardItem *parentItem =
nullptr;
353 QStringList parents = path;
354 while ( !parents.empty() )
356 const QString parentPath = parents.takeFirst();
358 QModelIndex thisParent;
359 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
361 const QModelIndex index =
mOptTreeModel->index( row, 0, parent );
362 if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0
363 || index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
371 if ( !thisParent.isValid() )
373 QStandardItem *newParentItem =
new QStandardItem( parentPath );
374 newParentItem->setToolTip( parentPath );
375 newParentItem->setSelectable(
false );
377 parentItem->appendRow( newParentItem );
380 parentItem = newParentItem;
392 parentItem->appendRow( item );
393 const QModelIndex newIndex =
mOptTreeModel->indexFromItem( item );
401 scrollArea->setWidgetResizable(
true );
402 scrollArea->setFrameShape( QFrame::NoFrame );
403 scrollArea->setObjectName( widget->objectName() );
404 scrollArea->setWidget( widget );
412void QgsOptionsDialogBase::insertPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QString &before,
const QStringList &path,
const QString &key )
418 if ( currentPage->objectName() == before )
424 QListWidgetItem *item =
new QListWidgetItem();
425 item->setIcon( icon );
426 item->setText( title );
427 item->setToolTip( tooltip );
432 QModelIndex sourceIndexBefore =
mTreeProxyModel->pageNumberToSourceIndex( page );
433 QList< QModelIndex > sourceBeforeIndices;
434 while ( sourceIndexBefore.parent().isValid() )
436 sourceBeforeIndices.insert( 0, sourceIndexBefore );
437 sourceIndexBefore = sourceIndexBefore.parent();
439 sourceBeforeIndices.insert( 0, sourceIndexBefore );
441 QStringList parentPaths = path;
443 QModelIndex parentIndex;
444 QStandardItem *parentItem =
nullptr;
445 while ( !parentPaths.empty() )
447 QString thisPath = parentPaths.takeFirst();
448 QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
450 if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0
451 || 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 )
565 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
567 const QModelIndex currentIndex =
mOptTreeModel->index( row, 0, parent );
568 if ( currentIndex.data().toString().contains( text, Qt::CaseInsensitive ) )
570 hiddenPages.insert(
mTreeProxyModel->sourceIndexToPageNumber( currentIndex ), false );
572 traverseModel( currentIndex );
575 traverseModel( QModelIndex() );
579 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
581 hiddenPages.insert( rsw.second,
false );
584 for (
auto it = hiddenPages.constBegin(); it != hiddenPages.constEnd(); ++it )
589 if (
mOptTreeView && text.length() >= minimumTextLength )
615 if ( !
mTreeProxyModel->filterAcceptsRow( currentSourceIndex.row(), currentSourceIndex.parent() ) )
617 std::function<QModelIndex(
const QModelIndex & )> traverseModel;
618 traverseModel = [&](
const QModelIndex & parent ) -> QModelIndex
622 const QModelIndex proxyIndex =
mTreeProxyModel->index( row, 0, parent );
623 const QModelIndex sourceIndex =
mTreeProxyModel->mapToSource( proxyIndex );
624 if (
mOptTreeModel->itemFromIndex( sourceIndex )->isSelectable() )
630 QModelIndex res = traverseModel( proxyIndex );
635 return QModelIndex();
638 const QModelIndex firstVisibleSourceIndex = traverseModel( QModelIndex() );
640 if ( firstVisibleSourceIndex.isValid() )
666 const QList< QWidget * > widgets =
mOptStackedWidget->widget( i )->findChildren<QWidget *>();
667 for ( QWidget *widget : widgets )
674 QHash<QWidget *, QgsOptionsDialogHighlightWidget *> customHighlightWidgets;
681 if ( customHighlightWidgets.contains( widget ) )
683 shw = customHighlightWidgets.value( widget );
693 QgsDebugMsgLevel( QStringLiteral(
"Registering: %1" ).arg( widget->objectName() ), 4 );
707 res->setToolTip( tooltip );
735 QDialog::showEvent( e );
743 QDialog::paintEvent( e );
750 if ( !itemText.isEmpty() )
752 setWindowTitle( QStringLiteral(
"%1 %2 %3" )
754 .arg( QChar( 0x2014 ) )
771 if ( optView->maximumWidth() != 16777215 )
772 optView->setMaximumWidth( 16777215 );
776 int iconWidth = optView->iconSize().width();
777 int snapToIconWidth = iconWidth + 32;
780 mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
783 int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
784 bool diffWidth = optView->minimumWidth() != newWidth;
787 optView->setMinimumWidth( newWidth );
789 if (
mIconOnly && ( diffWidth || optView->width() != newWidth ) )
791 splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
792 splitSizes[0] = newWidth;
839 if ( ( *it ).second == index )
848 QMessageBox::warning(
nullptr, tr(
"Missing Objects" ),
849 tr(
"Base options dialog could not be initialized.\n\n"
850 "Missing some of the .ui template objects:\n" )
851 +
" mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter,\n mOptionsListFrame",
858QgsOptionsProxyModel::QgsOptionsProxyModel( QObject *parent )
859 : QSortFilterProxyModel( parent )
861 setDynamicSortFilter(
true );
864void QgsOptionsProxyModel::setPageHidden(
int page,
bool hidden )
866 mHiddenPages[ page ] = hidden;
870QModelIndex QgsOptionsProxyModel::pageNumberToSourceIndex(
int page )
const
872 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
874 return QModelIndex();
876 int pagesRemaining = page;
877 std::function<QModelIndex(
const QModelIndex & )> traversePages;
880 traversePages = [&](
const QModelIndex & parent ) -> QModelIndex
882 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
884 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
885 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
887 if ( pagesRemaining == 0 )
890 else pagesRemaining--;
893 const QModelIndex res = traversePages( currentIndex );
897 return QModelIndex();
900 return traversePages( QModelIndex() );
903int QgsOptionsProxyModel::sourceIndexToPageNumber(
const QModelIndex &index )
const
905 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
911 std::function<int(
const QModelIndex & )> traverseModel;
914 traverseModel = [&](
const QModelIndex & parent ) ->
int
916 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
918 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
919 if ( currentIndex == index )
922 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
925 const int res = traverseModel( currentIndex );
932 return traverseModel( QModelIndex() );
935bool QgsOptionsProxyModel::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent )
const
937 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
941 const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );
943 const int pageNumber = sourceIndexToPageNumber( sourceIndex );
944 if ( !mHiddenPages.value( pageNumber,
false ) )
947 if ( sourceModel()->hasChildren( sourceIndex ) )
950 for (
int row = 0; row < sourceModel()->rowCount( sourceIndex ); ++row )
952 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 setShowSearchIcon(bool visible)
Define if a search icon shall be shown on the left of the image when no text is entered.
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.
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.
QList< QPair< QgsOptionsDialogHighlightWidget *, int > > mRegisteredSearchWidgets
~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.
This class is a composition of two QSettings instances:
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)