completion_widget.py
136 lines
| 5.1 KiB
| text/x-python
|
PythonLexer
epatters
|
r2602 | # System library imports | ||
from PyQt4 import QtCore, QtGui | ||||
class CompletionWidget(QtGui.QListWidget): | ||||
""" A widget for GUI tab completion. | ||||
""" | ||||
#-------------------------------------------------------------------------- | ||||
epatters
|
r2744 | # 'QObject' interface | ||
epatters
|
r2602 | #-------------------------------------------------------------------------- | ||
def __init__(self, parent): | ||||
""" Create a completion widget that is attached to the specified Qt | ||||
text edit widget. | ||||
""" | ||||
assert isinstance(parent, (QtGui.QTextEdit, QtGui.QPlainTextEdit)) | ||||
QtGui.QListWidget.__init__(self, parent) | ||||
self.setWindowFlags(QtCore.Qt.ToolTip | QtCore.Qt.WindowStaysOnTopHint) | ||||
self.setAttribute(QtCore.Qt.WA_StaticContents) | ||||
# Ensure that parent keeps focus when widget is displayed. | ||||
self.setFocusProxy(parent) | ||||
self.setFrameShadow(QtGui.QFrame.Plain) | ||||
self.setFrameShape(QtGui.QFrame.StyledPanel) | ||||
self.itemActivated.connect(self._complete_current) | ||||
epatters
|
r2744 | def eventFilter(self, obj, event): | ||
""" Reimplemented to handle keyboard input and to auto-hide when our | ||||
parent loses focus. | ||||
""" | ||||
if obj == self.parent(): | ||||
etype = event.type() | ||||
if etype == QtCore.QEvent.KeyPress: | ||||
key, text = event.key(), event.text() | ||||
if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, | ||||
QtCore.Qt.Key_Tab): | ||||
self._complete_current() | ||||
return True | ||||
elif key == QtCore.Qt.Key_Escape: | ||||
self.hide() | ||||
return True | ||||
elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, | ||||
QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown, | ||||
QtCore.Qt.Key_Home, QtCore.Qt.Key_End): | ||||
QtGui.QListWidget.keyPressEvent(self, event) | ||||
return True | ||||
elif etype == QtCore.QEvent.FocusOut: | ||||
self.hide() | ||||
return QtGui.QListWidget.eventFilter(self, obj, event) | ||||
#-------------------------------------------------------------------------- | ||||
# 'QWidget' interface | ||||
#-------------------------------------------------------------------------- | ||||
epatters
|
r2602 | def hideEvent(self, event): | ||
epatters
|
r2744 | """ Reimplemented to disconnect signal handlers and event filter. | ||
epatters
|
r2602 | """ | ||
QtGui.QListWidget.hideEvent(self, event) | ||||
epatters
|
r2744 | parent = self.parent() | ||
epatters
|
r2613 | try: | ||
epatters
|
r2744 | parent.cursorPositionChanged.disconnect(self._update_current) | ||
epatters
|
r2613 | except TypeError: | ||
pass | ||||
epatters
|
r2744 | parent.removeEventFilter(self) | ||
epatters
|
r2602 | |||
def showEvent(self, event): | ||||
epatters
|
r2744 | """ Reimplemented to connect signal handlers and event filter. | ||
epatters
|
r2602 | """ | ||
QtGui.QListWidget.showEvent(self, event) | ||||
epatters
|
r2744 | parent = self.parent() | ||
parent.cursorPositionChanged.connect(self._update_current) | ||||
parent.installEventFilter(self) | ||||
epatters
|
r2602 | |||
#-------------------------------------------------------------------------- | ||||
# 'CompletionWidget' interface | ||||
#-------------------------------------------------------------------------- | ||||
def show_items(self, cursor, items): | ||||
""" Shows the completion widget with 'items' at the position specified | ||||
by 'cursor'. | ||||
""" | ||||
text_edit = self.parent() | ||||
point = text_edit.cursorRect(cursor).bottomRight() | ||||
point = text_edit.mapToGlobal(point) | ||||
screen_rect = QtGui.QApplication.desktop().availableGeometry(self) | ||||
if screen_rect.size().height() - point.y() - self.height() < 0: | ||||
point = text_edit.mapToGlobal(text_edit.cursorRect().topRight()) | ||||
point.setY(point.y() - self.height()) | ||||
self.move(point) | ||||
self._start_position = cursor.position() | ||||
self.clear() | ||||
self.addItems(items) | ||||
self.setCurrentRow(0) | ||||
self.show() | ||||
#-------------------------------------------------------------------------- | ||||
# Protected interface | ||||
#-------------------------------------------------------------------------- | ||||
def _complete_current(self): | ||||
""" Perform the completion with the currently selected item. | ||||
""" | ||||
self._current_text_cursor().insertText(self.currentItem().text()) | ||||
self.hide() | ||||
def _current_text_cursor(self): | ||||
""" Returns a cursor with text between the start position and the | ||||
current position selected. | ||||
""" | ||||
cursor = self.parent().textCursor() | ||||
if cursor.position() >= self._start_position: | ||||
cursor.setPosition(self._start_position, | ||||
QtGui.QTextCursor.KeepAnchor) | ||||
return cursor | ||||
def _update_current(self): | ||||
""" Updates the current item based on the current text. | ||||
""" | ||||
epatters
|
r2720 | prefix = self._current_text_cursor().selection().toPlainText() | ||
epatters
|
r2602 | if prefix: | ||
items = self.findItems(prefix, (QtCore.Qt.MatchStartsWith | | ||||
QtCore.Qt.MatchCaseSensitive)) | ||||
if items: | ||||
self.setCurrentItem(items[0]) | ||||
else: | ||||
self.hide() | ||||
else: | ||||
self.hide() | ||||