completion_widget.py
133 lines
| 5.1 KiB
| text/x-python
|
PythonLexer
epatters
|
r2602 | # System library imports | ||
Evan Patterson
|
r3304 | from IPython.external.qt import QtCore, QtGui | ||
epatters
|
r2602 | |||
class CompletionWidget(QtGui.QListWidget): | ||||
""" A widget for GUI tab completion. | ||||
""" | ||||
#-------------------------------------------------------------------------- | ||||
epatters
|
r2744 | # 'QObject' interface | ||
epatters
|
r2602 | #-------------------------------------------------------------------------- | ||
epatters
|
r2982 | def __init__(self, text_edit): | ||
epatters
|
r2602 | """ Create a completion widget that is attached to the specified Qt | ||
text edit widget. | ||||
""" | ||||
epatters
|
r2982 | assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit)) | ||
super(CompletionWidget, self).__init__() | ||||
self._text_edit = text_edit | ||||
epatters
|
r2602 | |||
self.setAttribute(QtCore.Qt.WA_StaticContents) | ||||
epatters
|
r2982 | self.setWindowFlags(QtCore.Qt.ToolTip | QtCore.Qt.WindowStaysOnTopHint) | ||
epatters
|
r2602 | |||
epatters
|
r2982 | # Ensure that the text edit keeps focus when widget is displayed. | ||
self.setFocusProxy(self._text_edit) | ||||
epatters
|
r2602 | |||
self.setFrameShadow(QtGui.QFrame.Plain) | ||||
self.setFrameShape(QtGui.QFrame.StyledPanel) | ||||
self.itemActivated.connect(self._complete_current) | ||||
epatters
|
r2744 | def eventFilter(self, obj, event): | ||
epatters
|
r2982 | """ Reimplemented to handle keyboard input and to auto-hide when the | ||
text edit loses focus. | ||||
epatters
|
r2744 | """ | ||
epatters
|
r2982 | if obj == self._text_edit: | ||
epatters
|
r2744 | etype = event.type() | ||
if etype == QtCore.QEvent.KeyPress: | ||||
key, text = event.key(), event.text() | ||||
Bernardo B. Marques
|
r4872 | if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, | ||
epatters
|
r2744 | QtCore.Qt.Key_Tab): | ||
self._complete_current() | ||||
return True | ||||
elif key == QtCore.Qt.Key_Escape: | ||||
self.hide() | ||||
return True | ||||
Bernardo B. Marques
|
r4872 | elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, | ||
QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown, | ||||
epatters
|
r2744 | QtCore.Qt.Key_Home, QtCore.Qt.Key_End): | ||
epatters
|
r2982 | self.keyPressEvent(event) | ||
epatters
|
r2744 | return True | ||
elif etype == QtCore.QEvent.FocusOut: | ||||
self.hide() | ||||
epatters
|
r2982 | return super(CompletionWidget, self).eventFilter(obj, event) | ||
epatters
|
r2744 | |||
#-------------------------------------------------------------------------- | ||||
# 'QWidget' interface | ||||
#-------------------------------------------------------------------------- | ||||
epatters
|
r2602 | def hideEvent(self, event): | ||
epatters
|
r2744 | """ Reimplemented to disconnect signal handlers and event filter. | ||
epatters
|
r2602 | """ | ||
epatters
|
r2982 | super(CompletionWidget, self).hideEvent(event) | ||
self._text_edit.cursorPositionChanged.disconnect(self._update_current) | ||||
self._text_edit.removeEventFilter(self) | ||||
epatters
|
r2602 | |||
def showEvent(self, event): | ||||
epatters
|
r2744 | """ Reimplemented to connect signal handlers and event filter. | ||
epatters
|
r2602 | """ | ||
epatters
|
r2982 | super(CompletionWidget, self).showEvent(event) | ||
self._text_edit.cursorPositionChanged.connect(self._update_current) | ||||
self._text_edit.installEventFilter(self) | ||||
epatters
|
r2602 | |||
#-------------------------------------------------------------------------- | ||||
# 'CompletionWidget' interface | ||||
#-------------------------------------------------------------------------- | ||||
def show_items(self, cursor, items): | ||||
""" Shows the completion widget with 'items' at the position specified | ||||
by 'cursor'. | ||||
""" | ||||
epatters
|
r2982 | text_edit = self._text_edit | ||
epatters
|
r2602 | 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. | ||||
""" | ||||
epatters
|
r2982 | cursor = self._text_edit.textCursor() | ||
epatters
|
r2602 | if cursor.position() >= self._start_position: | ||
Bernardo B. Marques
|
r4872 | cursor.setPosition(self._start_position, | ||
epatters
|
r2602 | QtGui.QTextCursor.KeepAnchor) | ||
return cursor | ||||
Bernardo B. Marques
|
r4872 | |||
epatters
|
r2602 | 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: | ||
Bernardo B. Marques
|
r4872 | items = self.findItems(prefix, (QtCore.Qt.MatchStartsWith | | ||
epatters
|
r2602 | QtCore.Qt.MatchCaseSensitive)) | ||
if items: | ||||
self.setCurrentItem(items[0]) | ||||
else: | ||||
self.hide() | ||||
else: | ||||
self.hide() | ||||