Skip to content

Commit b2478c2

Browse files
committed
use fuzzy find for tab search
1 parent e73826f commit b2478c2

File tree

1 file changed

+59
-25
lines changed

1 file changed

+59
-25
lines changed

NodeGraphQt/widgets/tab_search.py

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#!/usr/bin/python
22
from .. import QtCore, QtWidgets, QtGui
3-
43
from .stylesheet import STYLE_TABSEARCH, STYLE_TABSEARCH_LIST, STYLE_QMENU
4+
from collections import OrderedDict
5+
import re
56

67

78
class TabSearchCompleter(QtWidgets.QCompleter):
@@ -112,6 +113,18 @@ def set_nodes(self, node_dict=None):
112113
self._completer.setModel(self._model)
113114

114115

116+
def fuzzyFinder(key, collection):
117+
suggestions = []
118+
pattern = '.*?'.join(key.lower())
119+
regex = re.compile(pattern)
120+
for item in collection:
121+
match = regex.search(item.lower())
122+
if match:
123+
suggestions.append((len(match.group()), match.start(), item))
124+
125+
return [x for _, _, x in sorted(suggestions)]
126+
127+
115128
class TabSearchMenuWidget(QtWidgets.QLineEdit):
116129
search_submitted = QtCore.Signal(str)
117130

@@ -132,7 +145,7 @@ def __init__(self, parent=None, node_dict=None):
132145
self.SearchMenu.addAction(searchWidget)
133146
self.SearchMenu.setStyleSheet(STYLE_QMENU)
134147

135-
self._actions = []
148+
self._actions = {}
136149
self._menus = {}
137150
self._searched_actions = []
138151

@@ -151,30 +164,32 @@ def _on_text_changed(self,text):
151164

152165
self._set_menu_visible(False)
153166

154-
self._searched_actions = [action for action in self._actions\
155-
if text.lower() in action.text().lower()]
167+
action_names = fuzzyFinder(text, self._actions.keys())
156168

169+
self._searched_actions = [self._actions[name] for name in action_names]
157170
self.SearchMenu.addActions(self._searched_actions)
158171

172+
if self._searched_actions:
173+
self.SearchMenu.setActiveAction(self._searched_actions[0])
174+
159175
def _clear_actions(self):
160176
for action in self._searched_actions:
161177
self.SearchMenu.removeAction(action)
162178
self._searched_actions = []
163179

164-
def _set_menu_visible(self,visible):
165-
for menu in self._menus.values():
166-
menu.menuAction().setVisible(visible)
180+
def _set_menu_visible(self, visible):
181+
[menu.menuAction().setVisible(visible) for menu in self._menus.values()]
167182

168183
def _close(self):
169184
self._set_menu_visible(False)
170185
self.SearchMenu.setVisible(False)
171186
self.SearchMenu.menuAction().setVisible(False)
172187

173188
def _show(self):
174-
self.SearchMenu.exec_(QtGui.QCursor.pos())
175189
self.setText("")
176190
self.setFocus()
177191
self._set_menu_visible(True)
192+
self.SearchMenu.exec_(QtGui.QCursor.pos())
178193

179194
def _on_search_submitted(self):
180195
action = self.sender()
@@ -192,33 +207,52 @@ def _on_search_submitted(self):
192207

193208
self._close()
194209

195-
def _generate_items_from_node_dict(self):
196-
node_names = sorted(self._node_dict.keys())
210+
def build_menu_tree(self):
197211
node_types = sorted(self._node_dict.values())
212+
node_names = sorted(self._node_dict.keys())
213+
menu_tree = OrderedDict()
198214

199-
self._menus.clear()
200-
self._actions.clear()
201-
self._searched_actions.clear()
202-
215+
max_depth = 0
203216
for node_type in node_types:
204-
menu_name = ".".join(node_type.split(".")[:-1])
205-
if menu_name not in self._menus.keys():
206-
new_menu = QtWidgets.QMenu(menu_name)
207-
new_menu.setStyleSheet(STYLE_QMENU)
208-
self._menus[menu_name] = new_menu
209-
self.SearchMenu.addMenu(new_menu)
217+
trees = node_type.split(".")
218+
trees.pop(-1)
219+
for depth, menu_name in enumerate(trees):
220+
menu_path = ".".join(trees[:depth+1])
221+
if depth in menu_tree.keys():
222+
if menu_name not in menu_tree[depth].keys():
223+
new_menu = QtWidgets.QMenu(menu_name)
224+
new_menu.setStyleSheet(STYLE_QMENU)
225+
menu_tree[depth][menu_path] = new_menu
226+
else:
227+
new_menu = QtWidgets.QMenu(menu_name)
228+
new_menu.setStyleSheet(STYLE_QMENU)
229+
menu_tree[depth] = {menu_path: new_menu}
230+
if depth > 0:
231+
new_menu.parentPath = ".".join(trees[:depth])
232+
233+
max_depth = max(max_depth,depth)
234+
235+
for i in range(max_depth+1):
236+
menus = menu_tree[i]
237+
for menu_path, menu in menus.items():
238+
self._menus[menu_path] = menu
239+
if i == 0:
240+
self.SearchMenu.addMenu(menu)
241+
else:
242+
parentMenu = self._menus[menu.parentPath]
243+
parentMenu.addMenu(menu)
210244

211245
for name in node_names:
212246
action = QtWidgets.QAction(name, self)
213247
action.setText(name)
214248
action.triggered.connect(self._on_search_submitted)
215-
self._actions.append(action)
249+
self._actions[name] = action
216250

217251
menu_name = self._node_dict[name]
218-
menu_name = ".".join(menu_name.split(".")[:-1])
252+
menu_path = ".".join(menu_name.split(".")[:-1])
219253

220-
if menu_name in self._menus.keys():
221-
self._menus[menu_name].addAction(action)
254+
if menu_path in self._menus.keys():
255+
self._menus[menu_path].addAction(action)
222256
else:
223257
self.SearchMenu.addAction(action)
224258

@@ -231,7 +265,7 @@ def set_nodes(self, node_dict=None):
231265
continue
232266
for node_id in node_types:
233267
self._node_dict['{} ({})'.format(name, node_id)] = node_id
234-
self._generate_items_from_node_dict()
268+
self.build_menu_tree()
235269

236270
self._show()
237271

0 commit comments

Comments
 (0)