~emihalac/mnemosyne-proj/mnemosyne-proj

« back to all changes in this revision

Viewing changes to mnemosyne/mnemosyne/pyqt_ui/tag_line_edit.py

  • Committer: Emilian Mihalache
  • Date: 2017-04-07 05:09:15 UTC
  • Revision ID: emihalac@gmail.com-20170407050915-4j5e1d5evt3vzmlm
Fix tag autocompletion when selection is done with mouse

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
#
4
4
 
5
5
from PyQt5 import QtCore, QtWidgets
 
6
from mnemosyne.pyqt_ui.tag_completer import TagCompleter
6
7
 
7
8
class TagLineEdit(QtWidgets.QLineEdit):
8
9
 
 
10
    completion_prefix_changed = QtCore.pyqtSignal(str)
 
11
 
9
12
    def __init__(self, parent = None):
10
13
        super(TagLineEdit, self).__init__(parent)
11
 
        # We need to have the popup object created in PyQt so that we can call
12
 
        # its protected methods (e.g. passing it the event() call).
13
 
        self.completer_popup_ = QtWidgets.QListView()
14
 
        self.last_highlighted_text_ = ""
15
 
 
16
 
    def setupCompleter(self):
17
 
        self.completer().setCompletionMode(\
18
 
            QtWidgets.QCompleter.PopupCompletion)
19
 
        # We need to store a Python reference to the popup object, otherwise
20
 
        # it will get destroyed and re-built as a Qt object (as opposed to 
21
 
        # a PyQt one), which would make its protected methods inaccessible 
22
 
        # again.
23
 
        self.completer().setPopup(self.completer_popup_)
24
 
        # Leaving the popup object to handle KeyUp/KeyDown events on its own 
25
 
        # makes it override the entire text of the QComboBox, deleting all 
26
 
        # previously-entered tags. We need to chase after it with a stick 
27
 
        # and make it give our tags back.
28
 
        self.completer().popup().installEventFilter(self)
29
 
        self.completer().highlighted[str].connect(self.handleHighlight)
30
 
 
31
 
    def keyPressEvent(self, key_event):
32
 
        super(TagLineEdit, self).keyPressEvent(key_event)
33
 
        completer = self.completer()
34
 
        if completer is None:
35
 
            return
36
 
        completer.setCompletionPrefix(self.lastTagPrefix())
37
 
        if len(completer.completionPrefix()) < 1:
38
 
            completer.popup().hide()
39
 
            return
40
 
        cursor_rect_width = completer.popup().sizeHintForColumn(0) + \
41
 
                completer.popup().verticalScrollBar().sizeHint().width()
42
 
        cr = self.cursorRect()
43
 
        cr.setWidth(cursor_rect_width)
44
 
        completer.complete(cr)
45
 
 
46
 
    def eventFilter(self, obj, event):
47
 
        if event.type() == QtCore.QEvent.KeyPress:
48
 
            event_key = event.key()
49
 
            if event_key == QtCore.Qt.Key_Up or \
50
 
               event_key == QtCore.Qt.Key_Down:
51
 
                old_text = self.text()
52
 
                # Handing the event to the popup object results in strange
53
 
                # behavior where sometimes the QComboBox text is changed 
54
 
                # even though selection has not been confirmed.
55
 
                obj.event(event)
56
 
                # ... which is why we have to restore it.
57
 
                self.setText(old_text)
58
 
                return True
59
 
            elif event_key == QtCore.Qt.Key_Return or \
60
 
                 event_key == QtCore.Qt.Key_Enter:
61
 
                obj.hide()
62
 
                self.insertCompletion(self.last_highlighted_text_)
63
 
                return True
64
 
        return False
65
 
 
66
 
    def handleHighlight(self, highlightedText):
67
 
        self.last_highlighted_text_ = highlightedText
68
 
 
69
 
    def insertCompletion(self, completion):
70
 
        full_text = self.text()
71
 
        cursor_position = self.cursorPosition()
72
 
        last_relevant_comma_index = full_text[0:cursor_position].rfind(",")
73
 
        replaced_text = full_text[:last_relevant_comma_index + 1]  +\
74
 
            completion + full_text[cursor_position:]
75
 
        self.setText(replaced_text)
76
 
 
77
 
    def lastTagPrefix(self):
78
 
        full_text = self.text()
79
 
        cursor_position = self.cursorPosition()
80
 
        last_relevant_comma_index = full_text[0:cursor_position].rfind(",")
81
 
        return full_text[last_relevant_comma_index + 1:cursor_position].strip()
82
 
    
83
 
    
 
 
b'\\ No newline at end of file'
 
14
 
 
15
        # Stored because when we get the 'activated' signal,
 
16
        # the cursor has already moved at the end of the lineedit.
 
17
        # This can be jarring to the user if they are inserting
 
18
        # a tag in the middle of the text.
 
19
        self.last_cursor_position_ = 0
 
20
        # Stored because the popup list generated by the completer
 
21
        # tends to report the wrong index when emitting activated().
 
22
        # Debugging this lead nowhere, so let's just treat the last
 
23
        # highlighted item as the completion when we get the 'activated'
 
24
        # signal.
 
25
        self.last_highlight_ = ""
 
26
 
 
27
        self.custom_completer_ = TagCompleter(self)
 
28
        self.custom_completer_.setWidget(self)
 
29
 
 
30
        self.textEdited.connect(self.self_text_changed)
 
31
        self.completion_prefix_changed.connect( \
 
32
            self.custom_completer_.handle_prefix_change)
 
33
        self.custom_completer_.activated[str].connect( \
 
34
            self.fill_in_completion)
 
35
        self.custom_completer_.highlighted[str].connect( \
 
36
            self.handle_completer_highlight_)
 
37
 
 
38
    def handle_completer_highlight_(self, text):
 
39
        self.last_highlight_ = text
 
40
        self.last_cursor_position_ = self.cursorPosition()
 
41
 
 
42
    def fill_in_completion(self, completion):
 
43
        # 'completion' is intentionally discarded due to often being
 
44
        # erroneously reported by the Qt popup. Last highlighted suggestion
 
45
        # is used instead.
 
46
        old_text = self.text()
 
47
        cursor_position = self.last_cursor_position_
 
48
        previous_comma_index = old_text[:cursor_position].rfind(",") + 1
 
49
        
 
50
        new_text = old_text[:previous_comma_index] + self.last_highlight_ + "," + \
 
51
            old_text[cursor_position:]
 
52
        self.setText(new_text)
 
53
        new_cursor_index = previous_comma_index + len(self.last_highlight_)
 
54
        self.setCursorPosition(previous_comma_index + len(self.last_highlight_) + 1)
 
55
 
 
56
    def refresh_completion_model(self, new_model):
 
57
        self.custom_completer_.refresh_completion_model(new_model)
 
58
 
 
59
    def self_text_changed(self, new_text):
 
60
        self.completion_prefix_changed.emit( \
 
61
            self.last_tag_prefix(new_text))
 
62
 
 
63
    def last_tag_prefix(self, text):
 
64
        cursor_position = self.cursorPosition()
 
65
        last_relevant_comma_index = text[0:cursor_position].rfind(",")
 
66
        return text[last_relevant_comma_index + 1:cursor_position].strip()