Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Font improvements, shortcut to restart, quick tab switching #6

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
93 changes: 93 additions & 0 deletions preditor/gui/command_palette/command_palette.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from __future__ import absolute_import

from functools import partial

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
self.setMinimumSize(400, 200)
self.uiCloseSCT = QShortcut(
Qt.Key_Escape, self, context=Qt.WidgetWithChildrenShortcut
)
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.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)

self.original_model_index = model.original_model_index

def activated(self):
current = self.uiResultsLIST.currentIndex()
self.selected.emit(current)
self.hide()

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 _canceled(self):
# Restore the original tab as the user didn't choose the new tab
self.canceled.emit(self.original_model_index)
self.hide()

def reposition(self):
pgeo = self.parent().geometry()
geo = self.geometry()
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)
33 changes: 33 additions & 0 deletions preditor/gui/command_palette/workbox_completer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from __future__ import absolute_import

from Qt.QtWidgets import QCompleter

from .workbox_item_model import WorkboxFuzzyFilterProxyModel


class WorkboxCompleter(QCompleter):
def __init__(self, *args, **kwargs):
super(WorkboxCompleter, self).__init__(*args, **kwargs)
# Set this to None to disable splitting of paths
self.split_char = "/"
# Create the proxy model that implemts fuzy searching
self.proxyModel = WorkboxFuzzyFilterProxyModel(self)
# Prevent the completer from removing results. This allows
# us to always see the filtered results from the proxy model.
self.setCompletionMode(self.UnfilteredPopupCompletion)

def setModel(self, model):
self.proxyModel.setSourceModel(model)
super().setModel(self.proxyModel)

def splitPath(self, path):
if self.split_char:
return path.split(self.split_char)
return [path]

def pathFromIndex(self, index):
return self.model().pathFromIndex(index)
# return self.proxyModel.sourceModel().pathFromIndex(index)

def updatePattern(self, patternStr):
self.proxyModel.setFuzzySearch(patternStr)
108 changes: 108 additions & 0 deletions preditor/gui/command_palette/workbox_item_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from __future__ import absolute_import

import re

from Qt.QtCore import QSortFilterProxyModel, Qt
from Qt.QtGui import QStandardItem, QStandardItemModel


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():
parts.append(self.data(index, Qt.DisplayRole))
index = index.parent()
if len(parts) == 1:
return ""
return "/".join([x for x in parts[::-1] if x])


class WorkboxTreeItemModel(WorkboxItemModel):
def process(self):
root = self.invisibleRootItem()
current_group = self.manager.currentIndex()
current_tab = self.manager.currentWidget().currentIndex()

prev_group = -1
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()
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):
"""Implements a fuzzy search filter proxy model."""

def __init__(self, parent=None):
super(WorkboxFuzzyFilterProxyModel, self).__init__(parent=parent)
self._fuzzy_regex = None

def setFuzzySearch(self, search):
search = '.*'.join(search)
# search = '.*{}.*'.format(search)
self._fuzzy_regex = re.compile(search, re.I)
self.invalidateFilter()

def filterAcceptsRow(self, sourceRow, sourceParent):
if self.filterKeyColumn() == 0 and self._fuzzy_regex:

index = self.sourceModel().index(sourceRow, 0, sourceParent)
data = self.sourceModel().data(index)
ret = bool(self._fuzzy_regex.search(data))
return ret

return super(WorkboxFuzzyFilterProxyModel, self).filterAcceptsRow(
sourceRow, sourceParent
)

def pathFromIndex(self, index):
parts = [""]
while index.isValid():
parts.append(self.data(index, Qt.DisplayRole))
index = index.parent()
if len(parts) == 1:
return ""
return "/".join([x for x in parts[::-1] if x])
Loading