diff --git a/src/src/controller/LayoutFilterProxyModel.h b/src/src/controller/LayoutFilterProxyModel.h index b9cf784..b3ea3ac 100644 --- a/src/src/controller/LayoutFilterProxyModel.h +++ b/src/src/controller/LayoutFilterProxyModel.h @@ -32,25 +32,19 @@ class LayoutFilterProxyModel : public QSortFilterProxyModel { invalidateFilter(); } -protected: - bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { - if (pattern_.isEmpty()) - return true; + bool hasPattern() const { return !pattern_.isEmpty(); } - QAbstractItemModel *src = sourceModel(); - if (!src) - return true; - - const QModelIndex idx = src->index(source_row, 0, source_parent); - if (!idx.isValid()) + // True when the node at sourceIndex is a direct match (by name, or by the + // contents of its already-built table) — i.e. not merely a kept ancestor. + bool nodeMatches(const QModelIndex &sourceIndex) const { + if (pattern_.isEmpty() || !sourceIndex.isValid()) return false; - const QString name = src->data(idx, Qt::DisplayRole).toString(); + const QString name = sourceIndex.data(Qt::DisplayRole).toString(); if (name.contains(pattern_, Qt::CaseInsensitive)) return true; - // Content search for nodes that have already been parsed. - const QVariant v = src->data(idx, Qt::UserRole + 1); + const QVariant v = sourceIndex.data(Qt::UserRole + 1); auto *node = reinterpret_cast(v.value()); if (node != nullptr && node->inited() && node->table()) { const std::string needle = pattern_.toLower().toStdString(); @@ -65,6 +59,19 @@ class LayoutFilterProxyModel : public QSortFilterProxyModel { return false; } +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { + if (pattern_.isEmpty()) + return true; + + QAbstractItemModel *src = sourceModel(); + if (!src) + return true; + + const QModelIndex idx = src->index(source_row, 0, source_parent); + return nodeMatches(idx); + } + private: static bool ContainsLower(const std::string &haystack, const std::string &needle_lower) { if (needle_lower.empty()) diff --git a/src/src/dock/LayoutDockWidget.cpp b/src/src/dock/LayoutDockWidget.cpp index b6e6ada..8d9ae11 100755 --- a/src/src/dock/LayoutDockWidget.cpp +++ b/src/src/dock/LayoutDockWidget.cpp @@ -48,6 +48,8 @@ LayoutDockWidget::LayoutDockWidget(QWidget *parent) : QDockWidget(parent) connect(searchEdit, &QLineEdit::textChanged, this, &LayoutDockWidget::onSearchTextChanged); + connect(searchEdit, &QLineEdit::returnPressed, + this, &LayoutDockWidget::goToNextMatch); connect(treeView, &QTreeView::clicked, this, &LayoutDockWidget::clickedTreeNode); connect(treeView, &QTreeView::activated, @@ -122,6 +124,44 @@ void LayoutDockWidget::populateTree() treeView->setFocus(Qt::OtherFocusReason); } +void LayoutDockWidget::collectMatches(const QModelIndex &proxyParent, QModelIndexList &out) const +{ + const int rows = proxyModel->rowCount(proxyParent); + for (int i = 0; i < rows; ++i) { + const QModelIndex proxyIdx = proxyModel->index(i, 0, proxyParent); + const QModelIndex sourceIdx = proxyModel->mapToSource(proxyIdx); + if (proxyModel->nodeMatches(sourceIdx)) { + out.push_back(proxyIdx); + } + collectMatches(proxyIdx, out); + } +} + +void LayoutDockWidget::goToNextMatch() +{ + if (!controller || !proxyModel->hasPattern()) + return; + + QModelIndexList matches; + collectMatches(QModelIndex(), matches); + if (matches.isEmpty()) + return; + + // Advance to the first match after the current selection, wrapping around. + const QModelIndex current = treeView->currentIndex(); + int next = 0; + for (int i = 0; i < matches.size(); ++i) { + if (matches[i] == current) { + next = (i + 1) % matches.size(); + break; + } + } + + const QModelIndex target = matches[next]; + treeView->setCurrentIndex(target); + treeView->scrollTo(target, QAbstractItemView::PositionAtCenter); +} + void LayoutDockWidget::onSearchTextChanged(const QString &text) { if(!proxyModel) diff --git a/src/src/dock/LayoutDockWidget.h b/src/src/dock/LayoutDockWidget.h index f6d2b5a..c686480 100755 --- a/src/src/dock/LayoutDockWidget.h +++ b/src/src/dock/LayoutDockWidget.h @@ -33,6 +33,8 @@ class LayoutDockWidget : public QDockWidget void showTreeIndex(const QModelIndex &index); void populateTree(); void buildAndShowNode(moex::ViewNode *node); + void collectMatches(const QModelIndex &proxyParent, QModelIndexList &out) const; + void goToNextMatch(); public: explicit LayoutDockWidget(QWidget *parent = 0);