Show collection search results as a ranked list

The Collections search hid non-matching rows, so the hits stayed spread
through the expanded folders. Searching "diode" against the shipped
collection left five levels of tree open, and the first visible hit was
"Avalanche diode bidirectional".

The same matches are now shown as a flat list that takes the tree's
place while a search is active, best match first:

  exact name            1000
  name starts with       800
  name contains          600, less the match position
  info field only        300
  less the name length, so "Diode" precedes "Diode Zener bidirectional"

"diode" now gives Diode, diode-tube, Diode bridge, Diodes Module Pilot
Wire, Photodiode. No new index: this ranks what match() already returns
against Qt::UserRole+1, the string built at startup from the name and
every element-info field. An element matching several "+" terms is
listed once, and each row's tooltip is its folder, so similar names can
be told apart.

Double click or Enter on a result places it, as in the tree. Down from
the search field moves into the results, so searching and placing needs
no mouse. Emptying the field brings the tree back.

The query and ranking live in rankedSearch(), which returns hits without
model indexes, so another list can reuse them.

Discussion #676.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 3d6a280f9608758527ef0ca2de7223a19dfbfa45)
This commit is contained in:
ispyisail
2026-08-08 17:25:07 +12:00
parent 92cbc8fd2a
commit 73ad43bd8f
2 changed files with 228 additions and 14 deletions
@@ -43,6 +43,22 @@ class ElementsTreeView;
and all action needed to use this widget.
This is the element collection widget used in the diagram editor.
*/
class QListView;
class QStandardItemModel;
/**
@brief One ranked hit from a collection search.
Carries everything a result row needs, so a list that is not backed by
the collection model can show it.
*/
struct ElementSearchHit
{
QString path; ///< collection path, enough to build an ElementsLocation
QString name; ///< display name
QString folder; ///< where it lives, for telling similar names apart
QIcon icon;
};
class ElementsCollectionWidget : public QWidget
{
Q_OBJECT
@@ -56,6 +72,8 @@ class ElementsCollectionWidget : public QWidget
void removeProject (QETProject *project);
void highlightUnusedElement();
void setCurrentLocation(const ElementsLocation &location);
QVector<ElementSearchHit> rankedSearch(const QString &text,
const QModelIndex &within = QModelIndex());
protected:
void leaveEvent(QEvent *event) override;
@@ -102,6 +120,10 @@ class ElementsCollectionWidget : public QWidget
private:
void locationWasSaved(const ElementsLocation& location);
void activateIndex(const QModelIndex &index);
void showFlatResults(const QVector<ElementSearchHit> &hits);
void clearFlatResults();
static int rankMatch(const QString &needle, const QString &name,
const QString &haystack);
private:
@@ -113,6 +135,9 @@ class ElementsCollectionWidget : public QWidget
ElementsTreeView *m_tree_view;
ElementsTreeView *m_macros_tree_view = nullptr;
QTabWidget *m_tab_widget = nullptr;
/// Flat ranked results, shown in place of the tree while searching
QListView *m_search_results = nullptr;
QStandardItemModel *m_search_model = nullptr;
QVBoxLayout *m_main_vlayout;
QMenu *m_context_menu;
QModelIndex m_index_at_context_menu;