summaryrefslogtreecommitdiff
blob: 3012d3138737228d50f8294530f9a16f1b2a508d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
From 9d162207ef01c5972e4bb718d390c494f0ad0241 Mon Sep 17 00:00:00 2001
From: John Lindgren <john@jlindgren.net>
Date: Tue, 4 Sep 2018 23:39:00 -0400
Subject: [PATCH] qtui: Fix slow searching on large playlists.  Closes: #819.

---
 src/qtui/playlist-qt.cc | 58 +++++++++++++++++++++++++++++------------
 src/qtui/playlist-qt.h  |  1 +
 2 files changed, 42 insertions(+), 17 deletions(-)

diff --git a/src/qtui/playlist-qt.cc b/src/qtui/playlist-qt.cc
index 28c480ead..750d87c37 100644
--- a/src/qtui/playlist-qt.cc
+++ b/src/qtui/playlist-qt.cc
@@ -89,6 +89,31 @@ int PlaylistWidget::indexToRow (const QModelIndex & index)
     return proxyModel->mapToSource (index).row ();
 }
 
+QModelIndex PlaylistWidget::visibleIndexNear (int row)
+{
+    QModelIndex index = rowToIndex (row);
+    if (index.isValid ())
+        return index;
+
+    int n_entries = m_playlist.n_entries ();
+
+    for (int r = row + 1; r < n_entries; r ++)
+    {
+        index = rowToIndex (r);
+        if (index.isValid ())
+            return index;
+    }
+
+    for (int r = row - 1; r >= 0; r --)
+    {
+        index = rowToIndex (r);
+        if (index.isValid ())
+            return index;
+    }
+
+    return index;
+}
+
 void PlaylistWidget::contextMenuEvent (QContextMenuEvent * event)
 {
     if (contextMenu)
@@ -379,33 +404,32 @@ void PlaylistWidget::playCurrentIndex ()
 
 void PlaylistWidget::setFilter (const char * text)
 {
+    // Save the current focus before filtering
+    int focus = m_playlist.get_focus ();
+
+    // Empty the model before updating the filter.  This prevents Qt from
+    // performing a series of "rows added" or "rows deleted" updates, which can
+    // be very slow (worst case O(N^2) complexity) on a large playlist.
+    model->entriesRemoved (0, model->rowCount ());
+
+    // Update the filter
     proxyModel->setFilter (text);
 
-    int focus = m_playlist.get_focus ();
-    QModelIndex index;
+    // Repopulate the model
+    model->entriesAdded (0, m_playlist.n_entries ());
 
-    // If there was a valid focus before filtering, Qt updates it for us via
-    // currentChanged().  If not, we will set focus on the first visible row.
+    // If the previously focused row is no longer visible with the new filter,
+    // try to find a nearby one that is, and focus it.
+    auto index = visibleIndexNear (focus);
 
-    if (focus >= 0)
-        index = rowToIndex (focus);
-    else
+    if (index.isValid ())
     {
-        if (! proxyModel->rowCount ())
-            return;
-
-        index = proxyModel->index (0, 0);
         focus = indexToRow (index);
         m_playlist.set_focus (focus);
-    }
-
-    if (! m_playlist.entry_selected (focus))
-    {
         m_playlist.select_all (false);
         m_playlist.select_entry (focus, true);
+        scrollTo (index);
     }
-
-    scrollTo (index);
 }
 
 void PlaylistWidget::setFirstVisibleColumn (int col)
diff --git a/src/qtui/playlist-qt.h b/src/qtui/playlist-qt.h
index a2894323c..df44205af 100644
--- a/src/qtui/playlist-qt.h
+++ b/src/qtui/playlist-qt.h
@@ -66,6 +66,7 @@ class PlaylistWidget : public QTreeView
 
     QModelIndex rowToIndex (int row);
     int indexToRow (const QModelIndex & index);
+    QModelIndex visibleIndexNear (int row);
 
     void getSelectedRanges (int rowsBefore, int rowsAfter,
      QItemSelection & selected, QItemSelection & deselected);