From 399dc323af70cd01d2a9e033a44b9b8c6d438634 Mon Sep 17 00:00:00 2001 From: GPayne Date: Sun, 6 Aug 2023 15:08:00 -0600 Subject: [PATCH] Add search text box and filtering capability to profile assets editor --- .../launcher/include/profile/assetsdialog.h | 35 ++++++- .../ext/launcher/src/profile/assetsdialog.cpp | 92 ++++++++++++++++--- 2 files changed, 112 insertions(+), 15 deletions(-) diff --git a/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h index 85714b26bb..5ad607efb2 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h @@ -26,12 +26,36 @@ #define __OPENSPACE_UI_LAUNCHER___ASSETSDIALOG___H__ #include - +#include +#include #include "assettreemodel.h" class QTextEdit; class QTreeView; +class SearchProxyModel : public QSortFilterProxyModel { +public: + /** + * Constructor for SearchProxyModel class + * + * \param parent The QObject* object that is the Qt parent of this object + */ + SearchProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {} + + /** + * Sets the regular expression pattern to apply to the filter + * + * \param pattern The QString reference containing the regex pattern + */ + void setFilterRegExp(const QString& pattern); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + +private: + bool acceptIndex(const QModelIndex& idx) const; +}; + class AssetsDialog final : public QDialog { Q_OBJECT public: @@ -48,12 +72,14 @@ public: AssetsDialog(QWidget* parent, openspace::Profile* profile, const std::string& assetBasePath, const std::string& userAssetBasePath); +private slots: + void searchTextChanged(const QString& text); + private: void createWidgets(); - + void setViewToBaseModel(); void parseSelections(); void selected(const QModelIndex&); - /// Creates a text summary of all assets and their paths QString createTextSummary(); void openAssetEditor(); @@ -61,7 +87,10 @@ private: openspace::Profile* _profile = nullptr; AssetTreeModel _assetTreeModel; QTreeView* _assetTree = nullptr; + QLineEdit* _searchTextBox = nullptr; QTextEdit* _summary = nullptr; + QSortFilterProxyModel* _assetProxyModel = nullptr; + SearchProxyModel* _searchProxyModel = nullptr; }; #endif // __OPENSPACE_UI_LAUNCHER___ASSETSDIALOG___H__ diff --git a/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp index 897b7c2e5f..dedc8deb20 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp @@ -62,7 +62,6 @@ namespace { void traverseToFindFilesystemMatch(AssetTreeModel& model, QModelIndex parent, int nRows, const std::string& path) { - int startIndex = 0; std::string token = "${USER_ASSETS}/"; if (path.find(token) == 0) { @@ -152,6 +151,15 @@ AssetsDialog::AssetsDialog(QWidget* parent, openspace::Profile* profile, newAssetButton->setDefault(false); container->addWidget(newAssetButton, 0, 2); layout->addLayout(container); + + QLabel* searchLabel = new QLabel("Search: "); + searchLabel->setObjectName("heading"); + _searchTextBox = new QLineEdit; + _searchTextBox->setClearButtonEnabled(true); + QBoxLayout* layoutSearchBox = new QHBoxLayout; + layoutSearchBox->addWidget(searchLabel); + layoutSearchBox->addWidget(_searchTextBox); + layout->addLayout(layoutSearchBox); } { _assetTree = new QTreeView; @@ -160,17 +168,7 @@ AssetsDialog::AssetsDialog(QWidget* parent, openspace::Profile* profile, "Enable checkbox to include an asset. Those assets highlighted in red are " "present in the profile but do not exist in this OpenSpace installation" ); - _assetTree->setAlternatingRowColors(true); - _assetTree->setModel(&_assetTreeModel); - _assetTree->setRootIndex(_assetTreeModel.index(-1, 0)); - _assetTree->setColumnWidth(1, 60); - _assetTree->setColumnWidth(0, _assetTree->width() - 60); - _assetTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - _assetTree->header()->setSectionResizeMode(1, QHeaderView::Fixed); - _assetTree->header()->setStretchLastSection(false); - _assetTree->setAnimated(true); - _assetTree->setSortingEnabled(false); - _assetTree->setSelectionMode(QAbstractItemView::SingleSelection); + setViewToBaseModel(); connect(_assetTree, &QTreeView::clicked, this, &AssetsDialog::selected); @@ -215,8 +213,31 @@ AssetsDialog::AssetsDialog(QWidget* parent, openspace::Profile* profile, buttons, &QDialogButtonBox::rejected, this, &AssetsDialog::reject ); + connect( + _searchTextBox, &QLineEdit::textChanged, + this, &AssetsDialog::searchTextChanged + ); layout->addWidget(buttons); } + { + _searchProxyModel = new SearchProxyModel(this); + _searchProxyModel->setSourceModel(&_assetTreeModel); + _assetProxyModel = qobject_cast(_searchProxyModel); + } +} + +void AssetsDialog::setViewToBaseModel() { + _assetTree->setAlternatingRowColors(true); + _assetTree->setModel(&_assetTreeModel); + _assetTree->setRootIndex(_assetTreeModel.index(-1, 0)); + _assetTree->setColumnWidth(1, 60); + _assetTree->setColumnWidth(0, _assetTree->width() - 60); + _assetTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + _assetTree->header()->setSectionResizeMode(1, QHeaderView::Fixed); + _assetTree->header()->setStretchLastSection(false); + _assetTree->setAnimated(true); + _assetTree->setSortingEnabled(false); + _assetTree->setSelectionMode(QAbstractItemView::SingleSelection); } QString AssetsDialog::createTextSummary() { @@ -259,3 +280,50 @@ void AssetsDialog::parseSelections() { void AssetsDialog::selected(const QModelIndex&) { _summary->setText(createTextSummary()); } + +void AssetsDialog::searchTextChanged(const QString& text) { + if (!_assetProxyModel) { + return; + } + if (text.isEmpty()) { + setViewToBaseModel(); + traverseToExpandSelectedItems( + *_assetTree, + _assetTreeModel, + _assetTreeModel.rowCount(_assetTreeModel.index(-1, 0)), + _assetTreeModel.index(-1, 0) + ); + } + else { + _assetTree->setModel(_searchProxyModel); + _assetProxyModel->setFilterRegExp(text); + _assetTree->expandAll(); + } +} + +void SearchProxyModel::setFilterRegExp(const QString& pattern) { + QRegExp regex(pattern, Qt::CaseInsensitive, QRegExp::FixedString); + QSortFilterProxyModel::setFilterRegExp(regex); +} + +bool SearchProxyModel::filterAcceptsRow(int sourceRow, + const QModelIndex& sourceParent) const +{ + QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent); + return acceptIndex(idx); +} + +bool SearchProxyModel::acceptIndex(const QModelIndex& idx) const { + if (idx.isValid()) { + QString text = idx.data(Qt::DisplayRole).toString(); + if (filterRegExp().indexIn(text) >= 0) { + return true; + } + for (int row = 0; row < idx.model()->rowCount(idx); ++row) { + if (acceptIndex(idx.model()->index(row, 0, idx))) { + return true; + } + } + } + return false; +}