20#include <QDialogButtonBox>
23#include <QListWidgetItem>
28#include <QStackedWidget>
30#include <QStandardItem>
45 : QDialog( parent, fl )
46 , mOptsKey( settingsKey )
47 , mSettings( settings )
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" ) );
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() )
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 )
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 );
346 QStandardItem *parentItem =
nullptr;
349 QStringList parents = path;
350 while ( !parents.empty() )
352 const QString parentPath = parents.takeFirst();
354 QModelIndex thisParent;
355 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
357 const QModelIndex index =
mOptTreeModel->index( row, 0, parent );
358 if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0
359 || index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
367 if ( !thisParent.isValid() )
369 QStandardItem *newParentItem =
new QStandardItem( parentPath );
370 newParentItem->setToolTip( parentPath );
371 newParentItem->setSelectable(
false );
373 parentItem->appendRow( newParentItem );
376 parentItem = newParentItem;
388 parentItem->appendRow( item );
389 const QModelIndex newIndex =
mOptTreeModel->indexFromItem( item );
408 if ( currentPage->objectName() == before )
414 QListWidgetItem *item =
new QListWidgetItem();
415 item->setIcon( icon );
416 item->setText( title );
417 item->setToolTip( tooltip );
422 QModelIndex sourceIndexBefore =
mTreeProxyModel->pageNumberToSourceIndex( page );
423 QList< QModelIndex > sourceBeforeIndices;
424 while ( sourceIndexBefore.parent().isValid() )
426 sourceBeforeIndices.insert( 0, sourceIndexBefore );
427 sourceIndexBefore = sourceIndexBefore.parent();
429 sourceBeforeIndices.insert( 0, sourceIndexBefore );
431 QStringList parentPaths = path;
433 QModelIndex parentIndex;
434 QStandardItem *parentItem =
nullptr;
435 while ( !parentPaths.empty() )
437 QString thisPath = parentPaths.takeFirst();
438 QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
440 if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0
441 || sourceIndex.data( Qt::UserRole + 1 ).toString().compare( thisPath, Qt::CaseInsensitive ) == 0 )
443 parentIndex = sourceIndex;
448 QStandardItem *newParentItem =
new QStandardItem( thisPath );
449 newParentItem->setToolTip( thisPath );
450 newParentItem->setSelectable(
false );
451 if ( sourceIndex.isValid() )
455 parentItem->insertRow( sourceIndex.row(), newParentItem );
457 mOptTreeModel->insertRow( sourceIndex.row(), newParentItem );
463 parentItem->appendRow( newParentItem );
467 parentItem = newParentItem;
471 QStandardItem *item =
new QStandardItem( icon, title );
472 item->setToolTip( tooltip );
475 if ( sourceBeforeIndices.empty() )
476 parentItem->appendRow( item );
479 parentItem->insertRow( sourceBeforeIndices.at( 0 ).row(), item );
494 addPage( title, tooltip, icon, widget, path );
499 const int minimumTextLength = 3;
516 if (
mOptListWidget->item( r )->text().contains( text, Qt::CaseInsensitive ) )
522 mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
528 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
536 QMap< int, bool > hiddenPages;
539 hiddenPages.insert( r, text.length() >= minimumTextLength );
542 std::function<void(
const QModelIndex & )> traverseModel;
544 traverseModel = [&](
const QModelIndex & parent )
546 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
548 const QModelIndex currentIndex =
mOptTreeModel->index( row, 0, parent );
549 if ( currentIndex.data().toString().contains( text, Qt::CaseInsensitive ) )
551 hiddenPages.insert(
mTreeProxyModel->sourceIndexToPageNumber( currentIndex ), false );
553 traverseModel( currentIndex );
556 traverseModel( QModelIndex() );
560 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
562 hiddenPages.insert( rsw.second,
false );
565 for (
auto it = hiddenPages.constBegin(); it != hiddenPages.constEnd(); ++it )
570 if (
mOptTreeView && text.length() >= minimumTextLength )
596 if ( !
mTreeProxyModel->filterAcceptsRow( currentSourceIndex.row(), currentSourceIndex.parent() ) )
598 std::function<QModelIndex(
const QModelIndex & )> traverseModel;
599 traverseModel = [&](
const QModelIndex & parent ) -> QModelIndex
603 const QModelIndex proxyIndex =
mTreeProxyModel->index( row, 0, parent );
604 const QModelIndex sourceIndex =
mTreeProxyModel->mapToSource( proxyIndex );
605 if (
mOptTreeModel->itemFromIndex( sourceIndex )->isSelectable() )
611 QModelIndex res = traverseModel( proxyIndex );
616 return QModelIndex();
619 const QModelIndex firstVisibleSourceIndex = traverseModel( QModelIndex() );
621 if ( firstVisibleSourceIndex.isValid() )
648 const QList< QWidget * > widgets =
mOptStackedWidget->widget( i )->findChildren<QWidget *>();
649 for ( QWidget *w : widgets )
652 QHash<QWidget *, QgsOptionsDialogHighlightWidget *> customHighlightWidgets;
660 if ( customHighlightWidgets.contains( w ) )
662 shw = customHighlightWidgets.value( w );
671 QgsDebugMsgLevel( QStringLiteral(
"Registering: %1" ).arg( w->objectName() ), 4 );
685 res->setToolTip( tooltip );
713 QDialog::showEvent( e );
721 QDialog::paintEvent( e );
728 if ( !itemText.isEmpty() )
730 setWindowTitle( QStringLiteral(
"%1 %2 %3" )
732 .arg( QChar( 0x2014 ) )
749 if ( optView->maximumWidth() != 16777215 )
750 optView->setMaximumWidth( 16777215 );
754 int iconWidth = optView->iconSize().width();
755 int snapToIconWidth = iconWidth + 32;
758 mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
761 int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
762 bool diffWidth = optView->minimumWidth() != newWidth;
765 optView->setMinimumWidth( newWidth );
767 if (
mIconOnly && ( diffWidth || optView->width() != newWidth ) )
769 splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
770 splitSizes[0] = newWidth;
817 if ( ( *it ).second == index )
826 QMessageBox::warning(
nullptr, tr(
"Missing Objects" ),
827 tr(
"Base options dialog could not be initialized.\n\n"
828 "Missing some of the .ui template objects:\n" )
829 +
" mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter,\n mOptionsListFrame",
836QgsOptionsProxyModel::QgsOptionsProxyModel( QObject *parent )
837 : QSortFilterProxyModel( parent )
839 setDynamicSortFilter(
true );
842void QgsOptionsProxyModel::setPageHidden(
int page,
bool hidden )
844 mHiddenPages[ page ] = hidden;
848QModelIndex QgsOptionsProxyModel::pageNumberToSourceIndex(
int page )
const
850 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
852 return QModelIndex();
854 int pagesRemaining = page;
855 std::function<QModelIndex(
const QModelIndex & )> traversePages;
858 traversePages = [&](
const QModelIndex & parent ) -> QModelIndex
860 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
862 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
863 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
865 if ( pagesRemaining == 0 )
868 else pagesRemaining--;
871 const QModelIndex res = traversePages( currentIndex );
875 return QModelIndex();
878 return traversePages( QModelIndex() );
881int QgsOptionsProxyModel::sourceIndexToPageNumber(
const QModelIndex &index )
const
883 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
889 std::function<int(
const QModelIndex & )> traverseModel;
892 traverseModel = [&](
const QModelIndex & parent ) ->
int
894 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
896 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
897 if ( currentIndex == index )
900 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
903 const int res = traverseModel( currentIndex );
910 return traverseModel( QModelIndex() );
913bool QgsOptionsProxyModel::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent )
const
915 QStandardItemModel *itemModel = qobject_cast< QStandardItemModel * >( sourceModel() );
919 const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );
921 const int pageNumber = sourceIndexToPageNumber( sourceIndex );
922 if ( !mHiddenPages.value( pageNumber,
false ) )
925 if ( sourceModel()->hasChildren( sourceIndex ) )
928 for (
int row = 0; row < sourceModel()->rowCount( sourceIndex ); ++row )
930 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:
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
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)