20#include <QDialogButtonBox>
23#include <QListWidgetItem>
28#include <QStackedWidget>
30#include <QStandardItem>
44 : QDialog( parent, fl )
45 , mOptsKey( settingsKey )
46 , mSettings( settings )
54 mSettings->setValue( QStringLiteral(
"/Windows/%1/geometry" ).arg(
mOptsKey ), saveGeometry() );
81 if ( title.isEmpty() )
88 if (
auto *lLayout = layout() )
90 lLayout->setContentsMargins( 0, 0, 0, 0 );
94 mOptListWidget = findChild<QListWidget *>( QStringLiteral(
"mOptionsListWidget" ) );
95 mOptTreeView = findChild<QTreeView *>( QStringLiteral(
"mOptionsTreeView" ) );
105 QFrame *optionsFrame = findChild<QFrame *>( QStringLiteral(
"mOptionsFrame" ) );
106 mOptStackedWidget = findChild<QStackedWidget *>( QStringLiteral(
"mOptionsStackedWidget" ) );
107 mOptSplitter = findChild<QSplitter *>( QStringLiteral(
"mOptionsSplitter" ) );
108 mOptButtonBox = findChild<QDialogButtonBox *>( QStringLiteral(
"buttonBox" ) );
109 QFrame *buttonBoxFrame = findChild<QFrame *>( QStringLiteral(
"mButtonBoxFrame" ) );
110 mSearchLineEdit = findChild<QgsFilterLineEdit *>( QStringLiteral(
"mSearchLineEdit" ) );
125 iconSize = size + iconBuffer;
132 optView->setIconSize( QSize( iconSize, iconSize ) );
133 optView->setFrameStyle( QFrame::NoFrame );
136 optionsFrame->layout()->setContentsMargins( 0, frameMargin, frameMargin, frameMargin );
137 QVBoxLayout *layout =
static_cast<QVBoxLayout *
>( optionsFrame->layout() );
139 if ( buttonBoxFrame )
141 buttonBoxFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
142 layout->insertWidget( layout->count(), buttonBoxFrame );
152 disconnect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
153 connect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
154 disconnect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
155 connect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
164 connect(
mOptTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
mOptStackedWidget, [ = ](
const QItemSelection &,
const QItemSelection & )
166 const QModelIndexList selected = mOptTreeView->selectionModel()->selectedIndexes();
167 if ( selected.isEmpty() )
170 const QModelIndex index = mTreeProxyModel->mapToSource( selected.at( 0 ) );
172 if ( !mOptTreeModel || !mOptTreeModel->itemFromIndex( index )->isSelectable() )
175 mOptStackedWidget->setCurrentIndex( mTreeProxyModel->sourceIndexToPageNumber( index ) );
213 if ( !title.isEmpty() )
224 restoreGeometry(
mSettings->value( QStringLiteral(
"/Windows/%1/geometry" ).arg(
mOptsKey ) ).toByteArray() );
230 optView->setMaximumWidth(
233 optView->setAttribute( Qt::WA_MacShowFocusRect,
false );
245 l->setContentsMargins( 0, 0, 0, 0 );
252 int curIndx =
mSettings->value( QStringLiteral(
"/Windows/%1/tab" ).arg(
mOptsKey ), 0 ).toInt();
273 setListToItemAtIndex( curIndx );
276void QgsOptionsDialogBase::setListToItemAtIndex(
int index )
295 QSizePolicy::Policy policy = QSizePolicy::Ignored;
298 policy = QSizePolicy::MinimumExpanding;
318 if ( currentPage->objectName() == page )
327void QgsOptionsDialogBase::addPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QStringList &path,
const QString &key )
333 QListWidgetItem *item =
new QListWidgetItem();
334 item->setIcon( icon );
335 item->setText( title );
336 item->setToolTip( tooltip );
341 QStandardItem *item =
new QStandardItem( icon, title );
342 item->setToolTip( tooltip );
343 if ( !key.isEmpty() )
345 item->setData( key );
349 QStandardItem *parentItem =
nullptr;
352 QStringList parents = path;
353 while ( !parents.empty() )
355 const QString parentPath = parents.takeFirst();
357 QModelIndex thisParent;
358 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
360 const QModelIndex index =
mOptTreeModel->index( row, 0, parent );
361 if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0
362 || index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
370 if ( !thisParent.isValid() )
372 QStandardItem *newParentItem =
new QStandardItem( parentPath );
373 newParentItem->setToolTip( parentPath );
374 newParentItem->setSelectable(
false );
376 parentItem->appendRow( newParentItem );
379 parentItem = newParentItem;
391 parentItem->appendRow( item );
392 const QModelIndex newIndex =
mOptTreeModel->indexFromItem( item );
405void QgsOptionsDialogBase::insertPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QString &before,
const QStringList &path,
const QString &key )
411 if ( currentPage->objectName() == before )
417 QListWidgetItem *item =
new QListWidgetItem();
418 item->setIcon( icon );
419 item->setText( title );
420 item->setToolTip( tooltip );
425 QModelIndex sourceIndexBefore =
mTreeProxyModel->pageNumberToSourceIndex( page );
426 QList< QModelIndex > sourceBeforeIndices;
427 while ( sourceIndexBefore.parent().isValid() )
429 sourceBeforeIndices.insert( 0, sourceIndexBefore );
430 sourceIndexBefore = sourceIndexBefore.parent();
432 sourceBeforeIndices.insert( 0, sourceIndexBefore );
434 QStringList parentPaths = path;
436 QModelIndex parentIndex;
437 QStandardItem *parentItem =
nullptr;
438 while ( !parentPaths.empty() )
440 QString thisPath = parentPaths.takeFirst();
441 QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
443 if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0
444 || sourceIndex.data( Qt::UserRole + 1 ).toString().compare( thisPath, Qt::CaseInsensitive ) == 0 )
446 parentIndex = sourceIndex;
451 QStandardItem *newParentItem =
new QStandardItem( thisPath );
452 newParentItem->setToolTip( thisPath );
453 newParentItem->setSelectable(
false );
454 if ( sourceIndex.isValid() )
458 parentItem->insertRow( sourceIndex.row(), newParentItem );
460 mOptTreeModel->insertRow( sourceIndex.row(), newParentItem );
466 parentItem->appendRow( newParentItem );
470 parentItem = newParentItem;
474 QStandardItem *item =
new QStandardItem( icon, title );
475 item->setToolTip( tooltip );
476 if ( !key.isEmpty() )
478 item->setData( key );
482 if ( sourceBeforeIndices.empty() )
483 parentItem->appendRow( item );
486 parentItem->insertRow( sourceBeforeIndices.at( 0 ).row(), item );
501 addPage( title, tooltip, icon, widget, path );
506 const int minimumTextLength = 3;
523 if (
mOptListWidget->item( r )->text().contains( text, Qt::CaseInsensitive ) )
529 mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
535 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
543 QMap< int, bool > hiddenPages;
546 hiddenPages.insert( r, text.length() >= minimumTextLength );
549 std::function<void(
const QModelIndex & )> traverseModel;
551 traverseModel = [&](
const QModelIndex & parent )
553 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
555 const QModelIndex currentIndex =
mOptTreeModel->index( row, 0, parent );
556 if ( currentIndex.data().toString().contains( text, Qt::CaseInsensitive ) )
558 hiddenPages.insert(
mTreeProxyModel->sourceIndexToPageNumber( currentIndex ), false );
560 traverseModel( currentIndex );
563 traverseModel( QModelIndex() );
567 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
569 hiddenPages.insert( rsw.second,
false );
572 for (
auto it = hiddenPages.constBegin(); it != hiddenPages.constEnd(); ++it )
577 if (
mOptTreeView && text.length() >= minimumTextLength )
603 if ( !
mTreeProxyModel->filterAcceptsRow( currentSourceIndex.row(), currentSourceIndex.parent() ) )
605 std::function<QModelIndex(
const QModelIndex & )> traverseModel;
606 traverseModel = [&](
const QModelIndex & parent ) -> QModelIndex
610 const QModelIndex proxyIndex =
mTreeProxyModel->index( row, 0, parent );
611 const QModelIndex sourceIndex =
mTreeProxyModel->mapToSource( proxyIndex );
612 if (
mOptTreeModel->itemFromIndex( sourceIndex )->isSelectable() )
618 QModelIndex res = traverseModel( proxyIndex );
623 return QModelIndex();
626 const QModelIndex firstVisibleSourceIndex = traverseModel( QModelIndex() );
628 if ( firstVisibleSourceIndex.isValid() )
654 const QList< QWidget * > widgets =
mOptStackedWidget->widget( i )->findChildren<QWidget *>();
655 for ( QWidget *widget : widgets )
662 QHash<QWidget *, QgsOptionsDialogHighlightWidget *> customHighlightWidgets;
669 if ( customHighlightWidgets.contains( widget ) )
671 shw = customHighlightWidgets.value( widget );
681 QgsDebugMsgLevel( QStringLiteral(
"Registering: %1" ).arg( widget->objectName() ), 4 );
695 res->setToolTip( tooltip );
723 QDialog::showEvent( e );
731 QDialog::paintEvent( e );
738 if ( !itemText.isEmpty() )
740 setWindowTitle( QStringLiteral(
"%1 %2 %3" )
742 .arg( QChar( 0x2014 ) )
759 if ( optView->maximumWidth() != 16777215 )
760 optView->setMaximumWidth( 16777215 );
764 int iconWidth = optView->iconSize().width();
765 int snapToIconWidth = iconWidth + 32;
768 mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
771 int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
772 bool diffWidth = optView->minimumWidth() != newWidth;
775 optView->setMinimumWidth( newWidth );
777 if (
mIconOnly && ( diffWidth || optView->width() != newWidth ) )
779 splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
780 splitSizes[0] = newWidth;
827 if ( ( *it ).second == index )
836 QMessageBox::warning(
nullptr, tr(
"Missing Objects" ),
837 tr(
"Base options dialog could not be initialized.\n\n"
838 "Missing some of the .ui template objects:\n" )
839 +
" mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter,\n mOptionsListFrame",
846QgsOptionsProxyModel::QgsOptionsProxyModel( QObject *parent )
847 : QSortFilterProxyModel( parent )
849 setDynamicSortFilter(
true );
852void QgsOptionsProxyModel::setPageHidden(
int page,
bool hidden )
854 mHiddenPages[ page ] = hidden;
858QModelIndex QgsOptionsProxyModel::pageNumberToSourceIndex(
int page )
const
860 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
862 return QModelIndex();
864 int pagesRemaining = page;
865 std::function<QModelIndex(
const QModelIndex & )> traversePages;
868 traversePages = [&](
const QModelIndex & parent ) -> QModelIndex
870 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
872 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
873 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
875 if ( pagesRemaining == 0 )
878 else pagesRemaining--;
881 const QModelIndex res = traversePages( currentIndex );
885 return QModelIndex();
888 return traversePages( QModelIndex() );
891int QgsOptionsProxyModel::sourceIndexToPageNumber(
const QModelIndex &index )
const
893 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
899 std::function<int(
const QModelIndex & )> traverseModel;
902 traverseModel = [&](
const QModelIndex & parent ) ->
int
904 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
906 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
907 if ( currentIndex == index )
910 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
913 const int res = traverseModel( currentIndex );
920 return traverseModel( QModelIndex() );
923bool QgsOptionsProxyModel::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent )
const
925 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
929 const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );
931 const int pageNumber = sourceIndexToPageNumber( sourceIndex );
932 if ( !mHiddenPages.value( pageNumber,
false ) )
935 if ( sourceModel()->hasChildren( sourceIndex ) )
938 for (
int row = 0; row < sourceModel()->rowCount( sourceIndex ); ++row )
940 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)
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)