Add new completion -> auto-open option.

Closes #557.
This commit is contained in:
Florian Bruhin 2015-06-18 07:58:15 +02:00
parent b3869fe42b
commit bf4e968c67
3 changed files with 89 additions and 18 deletions

View File

@ -19,7 +19,7 @@
"""Completer attached to a CompletionView.""" """Completer attached to a CompletionView."""
from PyQt5.QtCore import pyqtSlot, QObject, QTimer from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.commands import cmdutils, runners from qutebrowser.commands import cmdutils, runners
@ -40,14 +40,22 @@ class Completer(QObject):
_last_cursor_pos: The old cursor position so we avoid double completion _last_cursor_pos: The old cursor position so we avoid double completion
updates. updates.
_last_text: The old command text so we avoid double completion updates. _last_text: The old command text so we avoid double completion updates.
_signals_connected: Whether the signals are connected to update the
completion when the command widget requests that.
Signals:
next_prev_item: Emitted to select the next/previous item in the
completion.
arg0: True for the previous item, False for the next.
""" """
next_prev_item = pyqtSignal(bool)
def __init__(self, cmd, win_id, parent=None): def __init__(self, cmd, win_id, parent=None):
super().__init__(parent) super().__init__(parent)
self._win_id = win_id self._win_id = win_id
self._cmd = cmd self._cmd = cmd
self._cmd.update_completion.connect(self.schedule_completion_update) self._signals_connected = False
self._cmd.textEdited.connect(self.on_text_edited)
self._ignore_change = False self._ignore_change = False
self._empty_item_idx = None self._empty_item_idx = None
self._timer = QTimer() self._timer = QTimer()
@ -58,9 +66,63 @@ class Completer(QObject):
self._last_cursor_pos = None self._last_cursor_pos = None
self._last_text = None self._last_text = None
objreg.get('config').changed.connect(self.on_auto_open_changed)
self.handle_signal_connections()
self._cmd.clear_completion_selection.connect(
self.handle_signal_connections)
def __repr__(self): def __repr__(self):
return utils.get_repr(self) return utils.get_repr(self)
@config.change_filter('completion', 'auto-open')
def on_auto_open_changed(self):
self.handle_signal_connections()
@pyqtSlot()
def handle_signal_connections(self):
self._connect_signals(config.get('completion', 'auto-open'))
def _connect_signals(self, connect=True):
"""Connect or disconnect the completion signals.
Args:
connect: Whether to connect (True) or disconnect (False) the
signals.
Return:
True if the signals were connected (connect=True and aren't
connected yet) - otherwise False.
"""
connections = [
(self._cmd.update_completion, self.schedule_completion_update),
(self._cmd.textChanged, self.on_text_edited),
]
if connect and not self._signals_connected:
for sender, receiver in connections:
sender.connect(receiver)
self._signals_connected = True
return True
elif not connect:
for sender, receiver in connections:
try:
sender.disconnect(receiver)
except TypeError:
# Don't fail if not connected
pass
self._signals_connected = False
return False
def _open_completion_if_needed(self):
"""If auto-open is false, temporarily connect signals.
Also opens the completion.
"""
if not config.get('completion', 'auto-open'):
connected = self._connect_signals(True)
if connected:
self.update_completion()
def _model(self): def _model(self):
"""Convienience method to get the current completion model.""" """Convienience method to get the current completion model."""
completion = objreg.get('completion', scope='window', completion = objreg.get('completion', scope='window',
@ -405,3 +467,17 @@ class Completer(QObject):
# We also want to update the cursor part and emit update_completion # We also want to update the cursor part and emit update_completion
# here, but that's already done for us by cursorPositionChanged # here, but that's already done for us by cursorPositionChanged
# anyways, so we don't need to do it twice. # anyways, so we don't need to do it twice.
@cmdutils.register(instance='completer', hide=True,
modes=[usertypes.KeyMode.command], scope='window')
def completion_item_prev(self):
"""Select the previous completion item."""
self._open_completion_if_needed()
self.next_prev_item.emit(True)
@cmdutils.register(instance='completer', hide=True,
modes=[usertypes.KeyMode.command], scope='window')
def completion_item_next(self):
"""Select the next completion item."""
self._open_completion_if_needed()
self.next_prev_item.emit(False)

View File

@ -26,10 +26,9 @@ subclasses to provide completions.
from PyQt5.QtWidgets import QStyle, QTreeView, QSizePolicy from PyQt5.QtWidgets import QStyle, QTreeView, QSizePolicy
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel
from qutebrowser.commands import cmdutils
from qutebrowser.config import config, style from qutebrowser.config import config, style
from qutebrowser.completion import completiondelegate, completer from qutebrowser.completion import completiondelegate, completer
from qutebrowser.utils import usertypes, qtutils, objreg, utils from qutebrowser.utils import qtutils, objreg, utils
class CompletionView(QTreeView): class CompletionView(QTreeView):
@ -96,6 +95,7 @@ class CompletionView(QTreeView):
objreg.register('completion', self, scope='window', window=win_id) objreg.register('completion', self, scope='window', window=win_id)
cmd = objreg.get('status-command', scope='window', window=win_id) cmd = objreg.get('status-command', scope='window', window=win_id)
completer_obj = completer.Completer(cmd, win_id, self) completer_obj = completer.Completer(cmd, win_id, self)
completer_obj.next_prev_item.connect(self.on_next_prev_item)
objreg.register('completer', completer_obj, scope='window', objreg.register('completer', completer_obj, scope='window',
window=win_id) window=win_id)
self.enabled = config.get('completion', 'show') self.enabled = config.get('completion', 'show')
@ -168,12 +168,15 @@ class CompletionView(QTreeView):
# Item is a real item, not a category header -> success # Item is a real item, not a category header -> success
return idx return idx
def _next_prev_item(self, prev): @pyqtSlot(bool)
def on_next_prev_item(self, prev):
"""Handle a tab press for the CompletionView. """Handle a tab press for the CompletionView.
Select the previous/next item and write the new text to the Select the previous/next item and write the new text to the
statusbar. statusbar.
Called from the Completer's next_prev_item signal.
Args: Args:
prev: True for prev item, False for next one. prev: True for prev item, False for next one.
""" """
@ -233,18 +236,6 @@ class CompletionView(QTreeView):
selmod.clearSelection() selmod.clearSelection()
selmod.clearCurrentIndex() selmod.clearCurrentIndex()
@cmdutils.register(instance='completion', hide=True,
modes=[usertypes.KeyMode.command], scope='window')
def completion_item_prev(self):
"""Select the previous completion item."""
self._next_prev_item(prev=True)
@cmdutils.register(instance='completion', hide=True,
modes=[usertypes.KeyMode.command], scope='window')
def completion_item_next(self):
"""Select the next completion item."""
self._next_prev_item(prev=False)
def selectionChanged(self, selected, deselected): def selectionChanged(self, selected, deselected):
"""Extend selectionChanged to call completers selection_changed.""" """Extend selectionChanged to call completers selection_changed."""
super().selectionChanged(selected, deselected) super().selectionChanged(selected, deselected)

View File

@ -352,6 +352,10 @@ def data(readonly=False):
)), )),
('completion', sect.KeyValue( ('completion', sect.KeyValue(
('auto-open',
SettingValue(typ.Bool(), 'true'),
"Automatically open completion when typing."),
('download-path-suggestion', ('download-path-suggestion',
SettingValue(typ.DownloadPathSuggestion(), 'path'), SettingValue(typ.DownloadPathSuggestion(), 'path'),
"What to display in the download filename input."), "What to display in the download filename input."),