diff --git a/preditor/gui/command_palette/command_palette.py b/preditor/gui/command_palette/command_palette.py index 60ccc6bb..6e3417bc 100644 --- a/preditor/gui/command_palette/command_palette.py +++ b/preditor/gui/command_palette/command_palette.py @@ -1,64 +1,93 @@ from __future__ import absolute_import -import six -from Qt.QtCore import QPoint, Qt -from Qt.QtWidgets import QFrame, QHBoxLayout, QLineEdit, QShortcut +from functools import partial -from .workbox_completer import WorkboxCompleter +from Qt.QtCore import QModelIndex, QPoint, Qt, Signal +from Qt.QtWidgets import QFrame, QLineEdit, QListView, QShortcut, QVBoxLayout + +from .workbox_item_model import WorkboxFuzzyFilterProxyModel class CommandPalette(QFrame): + canceled = Signal("QModelIndex") + """Passes the original QModelIndex for the tab that was selected when the + widget was first shown. This lets you reset back to the orignal state.""" + highlighted = Signal("QModelIndex") + """Emitted when the user navitages to the given index, but hasn't selected.""" + selected = Signal("QModelIndex") + """Emitted when the user selects a item.""" + def __init__(self, model, parent=None, **kwargs): super(CommandPalette, self).__init__(parent=parent, **kwargs) self.y_offset = 100 - lyt = QHBoxLayout(self) - self.uiLineEDIT = QLineEdit(parent=self) - lyt.addWidget(self.uiLineEDIT) - self.setMinimumSize(400, self.sizeHint().height()) + self.setMinimumSize(400, 200) self.uiCloseSCT = QShortcut( Qt.Key_Escape, self, context=Qt.WidgetWithChildrenShortcut ) - self.uiCloseSCT.activated.connect(self.hide) - self.uiLineCOMPL = WorkboxCompleter() - self.uiLineCOMPL.split_char = None - self.uiLineCOMPL.setCaseSensitivity(False) - self.uiLineCOMPL.setModel(model) - self.uiLineEDIT.setCompleter(self.uiLineCOMPL) - self.uiLineCOMPL.activated.connect(self.completed) - self.uiLineCOMPL.highlighted.connect(self.completer_selected) - # self.uiLineCOMPL.popup().clicked.connect(self.completed) + self.uiCloseSCT.activated.connect(self._canceled) + + self.uiUpSCT = QShortcut(Qt.Key_Up, self, context=Qt.WidgetWithChildrenShortcut) + self.uiUpSCT.activated.connect(partial(self.increment_selection, -1)) + self.uiDownSCT = QShortcut( + Qt.Key_Down, self, context=Qt.WidgetWithChildrenShortcut + ) + self.uiDownSCT.activated.connect(partial(self.increment_selection, 1)) + + lyt = QVBoxLayout(self) + self.uiLineEDIT = QLineEdit(parent=self) self.uiLineEDIT.textChanged.connect(self.update_completer) - self.current_name = parent.name_for_workbox(parent.current_workbox()) + self.uiLineEDIT.returnPressed.connect(self.activated) + lyt.addWidget(self.uiLineEDIT) + self.uiResultsLIST = QListView(self) + self.uiResultsLIST.activated.connect(self.activated) + self.proxy_model = WorkboxFuzzyFilterProxyModel(self) + self.proxy_model.setSourceModel(model) + self.uiResultsLIST.setModel(self.proxy_model) + lyt.addWidget(self.uiResultsLIST) - def update_completer(self, wildcard): - self.uiLineCOMPL.updatePattern(wildcard) + self.original_model_index = model.original_model_index - def completed(self, name): - if isinstance(name, six.string_types): - self.current_name = name - else: - self.current_name = self.uiLineCOMPL.pathFromIndex(name) + def activated(self): + current = self.uiResultsLIST.currentIndex() + self.selected.emit(current) self.hide() - def completer_selected(self, name): - self.parent().workbox_for_name(name.rstrip("/"), visible=True) + def increment_selection(self, direction): + current = self.uiResultsLIST.currentIndex() + col = 0 + row = 0 + if current.isValid(): + col = current.column() + row = current.row() + direction + new = self.uiResultsLIST.model().index(row, col) + self.uiResultsLIST.setCurrentIndex(new) + self.highlighted.emit(new) + + def update_completer(self, wildcard): + if wildcard: + if not self.uiResultsLIST.currentIndex().isValid(): + new = self.uiResultsLIST.model().index(0, 0) + self.uiResultsLIST.setCurrentIndex(new) + else: + self.uiResultsLIST.clearSelection() + self.uiResultsLIST.setCurrentIndex(QModelIndex()) + self.proxy_model.setFuzzySearch(wildcard) + self.highlighted.emit(self.uiResultsLIST.currentIndex()) - def hide(self): - # Close the popup if its open - self.uiLineCOMPL.popup().hide() + def _canceled(self): # Restore the original tab as the user didn't choose the new tab - self.completer_selected(self.current_name) - super(CommandPalette, self).hide() + self.canceled.emit(self.original_model_index) + self.hide() def reposition(self): pgeo = self.parent().geometry() geo = self.geometry() - center = QPoint(pgeo.width() // 2, self.y_offset) + center = QPoint(pgeo.width() // 2, 0) geo.moveCenter(center) + geo.setY(self.y_offset) self.setGeometry(geo) def popup(self): + self.show() self.reposition() self.uiLineEDIT.setFocus(Qt.PopupFocusReason) - self.show() - self.uiLineCOMPL.complete() diff --git a/preditor/gui/command_palette/workbox_item_model.py b/preditor/gui/command_palette/workbox_item_model.py index ccea9f6b..f899211c 100644 --- a/preditor/gui/command_palette/workbox_item_model.py +++ b/preditor/gui/command_palette/workbox_item_model.py @@ -7,10 +7,20 @@ class WorkboxItemModel(QStandardItemModel): + GroupIndexRole = Qt.UserRole + 1 + TabIndexRole = GroupIndexRole + 1 + def __init__(self, manager, *args, **kwargs): super(WorkboxItemModel, self).__init__(*args, **kwargs) self.manager = manager + def workbox_indexes_from_model_index(self, index): + """Returns the group_index and tab_index for the provided QModelIndex""" + return ( + index.data(WorkboxListItemModel.GroupIndexRole), + index.data(WorkboxListItemModel.TabIndexRole), + ) + def pathFromIndex(self, index): parts = [""] while index.isValid(): @@ -24,23 +34,43 @@ def pathFromIndex(self, index): class WorkboxTreeItemModel(WorkboxItemModel): def process(self): root = self.invisibleRootItem() + current_group = self.manager.currentIndex() + current_tab = self.manager.currentWidget().currentIndex() + prev_group = -1 - for _, group_name, tab_name, group_index, _ in self.manager.all_widgets(): + all_widgets = self.manager.all_widgets() + for _, group_name, tab_name, group_index, tab_index in all_widgets: if prev_group != group_index: group_item = QStandardItem(group_name) + group_item.setData(group_index, self.GroupIndexRole) root.appendRow(group_item) prev_group = group_index tab_item = QStandardItem(tab_name) + tab_item.setData(group_index, self.GroupIndexRole) + tab_item.setData(tab_index, self.TabIndexRole) group_item.appendRow(tab_item) + if group_index == current_group and tab_index == current_tab: + self.original_model_index = self.indexFromItem(tab_item) class WorkboxListItemModel(WorkboxItemModel): + def flags(self, index): + return Qt.ItemIsEnabled | Qt.ItemIsSelectable + def process(self): root = self.invisibleRootItem() - for _, group_name, tab_name, _, _ in self.manager.all_widgets(): - group_item = QStandardItem('/'.join((group_name, tab_name))) - root.appendRow(group_item) + current_group = self.manager.currentIndex() + current_tab = self.manager.currentWidget().currentIndex() + + all_widgets = self.manager.all_widgets() + for _, group_name, tab_name, group_index, tab_index in all_widgets: + tab_item = QStandardItem('/'.join((group_name, tab_name))) + tab_item.setData(group_index, self.GroupIndexRole) + tab_item.setData(tab_index, self.TabIndexRole) + root.appendRow(tab_item) + if group_index == current_group and tab_index == current_tab: + self.original_model_index = self.indexFromItem(tab_item) class WorkboxFuzzyFilterProxyModel(QSortFilterProxyModel): diff --git a/preditor/gui/group_tab_widget/__init__.py b/preditor/gui/group_tab_widget/__init__.py index 4bae065b..6f2b3659 100644 --- a/preditor/gui/group_tab_widget/__init__.py +++ b/preditor/gui/group_tab_widget/__init__.py @@ -117,7 +117,7 @@ def all_widgets(self): """A generator yielding information about every widget under every group. Yields: - group tab name, widget tab name, group tab index, widget tab index, widget + widget, group tab name, widget tab name, group tab index, widget tab index """ for group_index in range(self.count()): group_name = self.tabText(group_index) diff --git a/preditor/gui/loggerwindow.py b/preditor/gui/loggerwindow.py index 7e9232d5..2ffa0202 100644 --- a/preditor/gui/loggerwindow.py +++ b/preditor/gui/loggerwindow.py @@ -1036,7 +1036,16 @@ def show_find_in_workboxes(self): def show_focus_name(self): model = WorkboxListItemModel(manager=self.uiWorkboxTAB) model.process() + + def update_tab(index): + group, tab = model.workbox_indexes_from_model_index(index) + if group is not None: + self.uiWorkboxTAB.set_current_groups_from_index(group, tab) + w = CommandPalette(model, parent=self) + w.selected.connect(update_tab) + w.canceled.connect(update_tab) + w.highlighted.connect(update_tab) w.popup() def updateCopyIndentsAsSpaces(self):