# -*- coding: utf-8 -*- """ This module contains the WordClickMode """ from pyqode.core.api.decoration import TextDecoration from pyqode.core.api.mode import Mode from pyqode.qt import QtCore, QtGui from pyqode.core.api import TextHelper, DelayJobRunner class WordClickMode(Mode, QtCore.QObject): """ Adds support for word click events. It will highlight the click-able word when the user press control and move the mouse over a word. Detecting whether a word is click-able is the responsability of the subclasses. You must override ``_check_word_cursor`` and call ``_select_word_cursor`` if this is a click-able word (this process might be asynchrone) otherwise _clear_selection. :attr:`pyqode.core.modes.WordClickMode.word_clicked` is emitted when the word is clicked by the user (while keeping control pressed). """ #: Signal emitted when a word is clicked. The parameter is a #: QTextCursor with the clicked word set as the selected text. word_clicked = QtCore.Signal(QtGui.QTextCursor) def __init__(self): QtCore.QObject.__init__(self) Mode.__init__(self) self._previous_cursor_start = -1 self._previous_cursor_end = -1 self._deco = None self._cursor = None self._timer = DelayJobRunner(delay=200) def on_state_changed(self, state): if state: self.editor.mouse_moved.connect(self._on_mouse_moved) self.editor.mouse_pressed.connect(self._on_mouse_pressed) self.editor.key_released.connect(self._on_key_released) self.editor.mouse_double_clicked.connect( self._on_mouse_double_clicked) else: self.editor.mouse_moved.disconnect(self._on_mouse_moved) self.editor.mouse_pressed.disconnect(self._on_mouse_pressed) self.editor.mouse_double_clicked.disconnect( self._on_mouse_double_clicked) def _on_mouse_double_clicked(self): self._timer.cancel_requests() def _on_key_released(self, event): if event.key() == QtCore.Qt.Key_Control: self._clear_selection() self._cursor = None def _select_word_cursor(self): """ Selects the word under the mouse cursor. """ cursor = TextHelper(self.editor).word_under_mouse_cursor() if (self._previous_cursor_start != cursor.selectionStart() and self._previous_cursor_end != cursor.selectionEnd()): self._remove_decoration() self._add_decoration(cursor) self._previous_cursor_start = cursor.selectionStart() self._previous_cursor_end = cursor.selectionEnd() def _clear_selection(self): try: self._remove_decoration() except ValueError: pass self.editor.set_mouse_cursor(QtCore.Qt.IBeamCursor) self._previous_cursor_start = -1 self._previous_cursor_end = -1 def _on_mouse_moved(self, event): """ mouse moved callback """ if event.modifiers() & QtCore.Qt.ControlModifier: cursor = TextHelper(self.editor).word_under_mouse_cursor() if (not self._cursor or cursor.position() != self._cursor.position()): self._check_word_cursor(cursor) self._cursor = cursor else: self._cursor = None self._clear_selection() def _check_word_under_mouse_cursor(self, cursor): raise NotImplementedError() def _on_mouse_pressed(self, event): """ mouse pressed callback """ if event.button() == 1 and self._deco: cursor = TextHelper(self.editor).word_under_mouse_cursor() if cursor and cursor.selectedText(): self._timer.request_job( self.word_clicked.emit, cursor) def _add_decoration(self, cursor): """ Adds a decoration for the word under ``cursor``. """ if self._deco is None: if cursor.selectedText(): self._deco = TextDecoration(cursor) if self.editor.background.lightness() < 128: self._deco.set_foreground(QtGui.QColor('#0681e0')) else: self._deco.set_foreground(QtCore.Qt.blue) self._deco.set_as_underlined() self.editor.decorations.append(self._deco) self.editor.set_mouse_cursor(QtCore.Qt.PointingHandCursor) else: self.editor.set_mouse_cursor(QtCore.Qt.IBeamCursor) def _remove_decoration(self): """ Removes the word under cursor's decoration """ if self._deco is not None: self.editor.decorations.remove(self._deco) self._deco = None