20 #include <QDialogButtonBox>
22 #include <QListWidget>
23 #include <QListWidgetItem>
24 #include <QMessageBox>
28 #include <QStackedWidget>
30 #include <QStandardItem>
32 #include <QHeaderView>
44 : QDialog( parent, fl )
45 , mOptsKey( settingsKey )
46 , mSettings( settings )
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" ) );
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() )
230 optView->setMaximumWidth(
231 mSettings->value( QStringLiteral(
"/Windows/%1/splitState" ).arg(
mOptsKey ) ).isNull() ? 150 : 16777215 );
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 );
276 void QgsOptionsDialogBase::setListToItemAtIndex(
int index )
295 QSizePolicy::Policy policy = QSizePolicy::Ignored;
298 policy = QSizePolicy::MinimumExpanding;
318 if ( currentPage->objectName() == page )
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 );
345 QStandardItem *parentItem =
nullptr;
348 QStringList parents = path;
349 while ( !parents.empty() )
351 const QString parentPath = parents.takeFirst();
353 QModelIndex thisParent;
354 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
356 const QModelIndex index =
mOptTreeModel->index( row, 0, parent );
357 if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0
358 || index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
366 if ( !thisParent.isValid() )
368 QStandardItem *newParentItem =
new QStandardItem( parentPath );
369 newParentItem->setToolTip( parentPath );
370 newParentItem->setSelectable(
false );
372 parentItem->appendRow( newParentItem );
375 parentItem = newParentItem;
387 parentItem->appendRow( item );
388 const QModelIndex newIndex =
mOptTreeModel->indexFromItem( item );
407 if ( currentPage->objectName() == before )
413 QListWidgetItem *item =
new QListWidgetItem();
414 item->setIcon( icon );
415 item->setText( title );
416 item->setToolTip( tooltip );
421 QModelIndex sourceIndexBefore =
mTreeProxyModel->pageNumberToSourceIndex( page );
422 QList< QModelIndex > sourceBeforeIndices;
423 while ( sourceIndexBefore.parent().isValid() )
425 sourceBeforeIndices.insert( 0, sourceIndexBefore );
426 sourceIndexBefore = sourceIndexBefore.parent();
428 sourceBeforeIndices.insert( 0, sourceIndexBefore );
430 QStringList parentPaths = path;
432 QModelIndex parentIndex;
433 QStandardItem *parentItem =
nullptr;
434 while ( !parentPaths.empty() )
436 QString thisPath = parentPaths.takeFirst();
437 QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
439 if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0
440 || sourceIndex.data( Qt::UserRole + 1 ).toString().compare( thisPath, Qt::CaseInsensitive ) == 0 )
442 parentIndex = sourceIndex;
447 QStandardItem *newParentItem =
new QStandardItem( thisPath );
448 newParentItem->setToolTip( thisPath );
449 newParentItem->setSelectable(
false );
450 if ( sourceIndex.isValid() )
454 parentItem->insertRow( sourceIndex.row(), newParentItem );
456 mOptTreeModel->insertRow( sourceIndex.row(), newParentItem );
462 parentItem->appendRow( newParentItem );
466 parentItem = newParentItem;
470 QStandardItem *item =
new QStandardItem( icon, title );
471 item->setToolTip( tooltip );
474 if ( sourceBeforeIndices.empty() )
475 parentItem->appendRow( item );
478 parentItem->insertRow( sourceBeforeIndices.at( 0 ).row(), item );
493 addPage( title, tooltip, icon, widget, path );
498 const int minimumTextLength = 3;
515 if (
mOptListWidget->item( r )->text().contains( text, Qt::CaseInsensitive ) )
521 mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
527 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
535 QMap< int, bool > hiddenPages;
538 hiddenPages.insert( r, text.length() >= minimumTextLength );
541 std::function<void(
const QModelIndex & )> traverseModel;
543 traverseModel = [&](
const QModelIndex & parent )
545 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
547 const QModelIndex currentIndex =
mOptTreeModel->index( row, 0, parent );
548 if ( currentIndex.data().toString().contains( text, Qt::CaseInsensitive ) )
550 hiddenPages.insert(
mTreeProxyModel->sourceIndexToPageNumber( currentIndex ), false );
552 traverseModel( currentIndex );
555 traverseModel( QModelIndex() );
559 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
561 hiddenPages.insert( rsw.second,
false );
564 for (
auto it = hiddenPages.constBegin(); it != hiddenPages.constEnd(); ++it )
569 if (
mOptTreeView && text.length() >= minimumTextLength )
595 if ( !
mTreeProxyModel->filterAcceptsRow( currentSourceIndex.row(), currentSourceIndex.parent() ) )
597 std::function<QModelIndex(
const QModelIndex & )> traverseModel;
598 traverseModel = [&](
const QModelIndex & parent ) -> QModelIndex
602 const QModelIndex proxyIndex =
mTreeProxyModel->index( row, 0, parent );
603 const QModelIndex sourceIndex =
mTreeProxyModel->mapToSource( proxyIndex );
604 if (
mOptTreeModel->itemFromIndex( sourceIndex )->isSelectable() )
610 QModelIndex res = traverseModel( proxyIndex );
615 return QModelIndex();
618 const QModelIndex firstVisibleSourceIndex = traverseModel( QModelIndex() );
620 if ( firstVisibleSourceIndex.isValid() )
647 const QList< QWidget * > widgets =
mOptStackedWidget->widget( i )->findChildren<QWidget *>();
648 for ( QWidget *w : widgets )
651 QHash<QWidget *, QgsOptionsDialogHighlightWidget *> customHighlightWidgets;
659 if ( customHighlightWidgets.contains( w ) )
661 shw = customHighlightWidgets.value( w );
670 QgsDebugMsgLevel( QStringLiteral(
"Registering: %1" ).arg( w->objectName() ), 4 );
684 res->setToolTip( tooltip );
712 QDialog::showEvent( e );
720 QDialog::paintEvent( e );
727 if ( !itemText.isEmpty() )
729 setWindowTitle( QStringLiteral(
"%1 %2 %3" )
731 .arg( QChar( 0x2014 ) )
748 if ( optView->maximumWidth() != 16777215 )
749 optView->setMaximumWidth( 16777215 );
753 int iconWidth = optView->iconSize().width();
754 int snapToIconWidth = iconWidth + 32;
757 mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
760 int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
761 bool diffWidth = optView->minimumWidth() != newWidth;
764 optView->setMinimumWidth( newWidth );
766 if (
mIconOnly && ( diffWidth || optView->width() != newWidth ) )
768 splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
769 splitSizes[0] = newWidth;
816 if ( ( *it ).second == index )
825 QMessageBox::warning(
nullptr, tr(
"Missing Objects" ),
826 tr(
"Base options dialog could not be initialized.\n\n"
827 "Missing some of the .ui template objects:\n" )
828 +
" mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter,\n mOptionsListFrame",
835 QgsOptionsProxyModel::QgsOptionsProxyModel( QObject *parent )
836 : QSortFilterProxyModel( parent )
838 setDynamicSortFilter(
true );
841 void QgsOptionsProxyModel::setPageHidden(
int page,
bool hidden )
843 mHiddenPages[ page ] = hidden;
847 QModelIndex QgsOptionsProxyModel::pageNumberToSourceIndex(
int page )
const
849 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
851 return QModelIndex();
853 int pagesRemaining = page;
854 std::function<QModelIndex(
const QModelIndex & )> traversePages;
857 traversePages = [&](
const QModelIndex & parent ) -> QModelIndex
859 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
861 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
862 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
864 if ( pagesRemaining == 0 )
867 else pagesRemaining--;
870 const QModelIndex res = traversePages( currentIndex );
874 return QModelIndex();
877 return traversePages( QModelIndex() );
880 int QgsOptionsProxyModel::sourceIndexToPageNumber(
const QModelIndex &index )
const
882 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
888 std::function<int(
const QModelIndex & )> traverseModel;
891 traverseModel = [&](
const QModelIndex & parent ) ->
int
893 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
895 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
896 if ( currentIndex == index )
899 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
902 const int res = traverseModel( currentIndex );
909 return traverseModel( QModelIndex() );
912 bool QgsOptionsProxyModel::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent )
const
914 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
918 const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );
920 const int pageNumber = sourceIndexToPageNumber( sourceIndex );
921 if ( !mHiddenPages.value( pageNumber,
false ) )
924 if ( sourceModel()->hasChildren( sourceIndex ) )
927 for (
int row = 0; row < sourceModel()->rowCount( sourceIndex ); ++row )
929 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 addPage(const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QStringList &path=QStringList())
Adds a new page to the dialog pages.
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
void insertPage(const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QString &before, const QStringList &path=QStringList())
Inserts a new page into the dialog pages.
~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
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 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:
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
#define QgsDebugMsgLevel(str, level)