diff --git a/qutebrowser/app.py b/qutebrowser/app.py index f50487311..5f94dbc07 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -129,6 +129,7 @@ class QuteBrowser(QApplication): 'hint': HintKeyParser(self), 'insert': PassthroughKeyParser('keybind.insert', self), 'passthrough': PassthroughKeyParser('keybind.passthrough', self), + 'command': PassthroughKeyParser('keybind.command', self), } self._init_cmds() self.mainwindow = MainWindow() @@ -140,7 +141,8 @@ class QuteBrowser(QApplication): modes.manager.register('passthrough', self._keyparsers['passthrough'].handle, passthrough=True) - modes.manager.register('command', None, passthrough=True) + modes.manager.register('command', self._keyparsers['command'].handle, + passthrough=True) self.modeman = modes.manager # for commands self.installEventFilter(modes.manager) self.setQuitOnLastWindowClosed(False) @@ -302,12 +304,11 @@ class QuteBrowser(QApplication): tabs.cur_link_hovered.connect(status.url.set_hover_url) # command input / completion - cmd.esc_pressed.connect(tabs.setFocus) + modes.manager.left.connect(tabs.on_mode_left) cmd.clear_completion_selection.connect( completion.on_clear_completion_selection) cmd.hide_completion.connect(completion.hide) cmd.textChanged.connect(completion.on_cmd_text_changed) - cmd.tab_pressed.connect(completion.on_tab_pressed) completion.change_completed_part.connect(cmd.on_change_completed_part) def _recover_pages(self): diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index d8ae8a858..652ae0376 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -89,7 +89,12 @@ SECTION_DESC = { "supported in this mode.\n" "An useful command to map here is the hidden command leave_mode."), 'keybind.passthrough': ( - "Keybindings for hint passthrough.\n" + "Keybindings for hint mode.\n" + "Since normal keypresses are passed through, only special keys are " + "supported in this mode.\n" + "An useful command to map here is the hidden command leave_mode."), + 'keybind.command': ( + "Keybindings for command mode.\n" "Since normal keypresses are passed through, only special keys are " "supported in this mode.\n" "An useful command to map here is the hidden command leave_mode."), @@ -448,6 +453,15 @@ DATA = OrderedDict([ ('', 'leave_mode'), )), + ('keybind.command', sect.ValueList( + types.KeyBindingName(), types.KeyBinding(), + ('', 'leave_mode'), + ('', 'command_history_prev'), + ('', 'command_history_next'), + ('', 'command_item_prev'), + ('', 'command_item_next'), + )), + ('aliases', sect.ValueList( types.Command(), types.Command(), )), diff --git a/qutebrowser/widgets/completion.py b/qutebrowser/widgets/completion.py index 7c80c1d95..24238790c 100644 --- a/qutebrowser/widgets/completion.py +++ b/qutebrowser/widgets/completion.py @@ -199,6 +199,30 @@ class CompletionView(QTreeView): logging.debug("No completion model for {}.".format(compl)) return None + def _next_prev_item(self, prev): + """Handle a tab press for the CompletionView. + + Select the previous/next item and write the new text to the + statusbar. + + Args: + prev: True for prev item, False for next one. + + Emit: + change_completed_part: When a completion took place. + """ + if not self._completing: + # No completion running at the moment, ignore keypress + return + idx = self._next_idx(prev) + self.selectionModel().setCurrentIndex( + idx, QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Rows) + data = self._model.data(idx) + if data is not None: + self._ignore_next = True + self.change_completed_part.emit(data) + def set_model(self, model): """Switch completion to a new model. @@ -265,31 +289,6 @@ class CompletionView(QTreeView): if self._enabled: self.show() - @pyqtSlot(bool) - def on_tab_pressed(self, shift): - """Handle a tab press for the CompletionView. - - Select the previous/next item and write the new text to the - statusbar. Called by key_(s)tab_handler in statusbar.command. - - Args: - shift: Whether shift is pressed or not. - - Emit: - change_completed_part: When a completion took place. - """ - if not self._completing: - # No completion running at the moment, ignore keypress - return - idx = self._next_idx(shift) - self.selectionModel().setCurrentIndex( - idx, QItemSelectionModel.ClearAndSelect | - QItemSelectionModel.Rows) - data = self._model.data(idx) - if data is not None: - self._ignore_next = True - self.change_completed_part.emit(data) - @pyqtSlot() def on_clear_completion_selection(self): """Clear the selection model when an item is activated.""" @@ -298,6 +297,16 @@ class CompletionView(QTreeView): selmod.clearSelection() selmod.clearCurrentIndex() + @cmdutils.register(instance='mainwindow.completion') + def command_item_prev(self): + """Select the previous completion item.""" + self._next_prev_item(prev=True) + + @cmdutils.register(instance='mainwindow.completion') + def command_item_next(self): + """Select the next completion item.""" + self._next_prev_item(prev=False) + class _CompletionItemDelegate(QStyledItemDelegate): diff --git a/qutebrowser/widgets/statusbar.py b/qutebrowser/widgets/statusbar.py index 91479f7e9..1890869b8 100644 --- a/qutebrowser/widgets/statusbar.py +++ b/qutebrowser/widgets/statusbar.py @@ -24,6 +24,7 @@ from PyQt5.QtWidgets import (QWidget, QLineEdit, QProgressBar, QLabel, from PyQt5.QtGui import QPainter, QKeySequence, QValidator import qutebrowser.keyinput.modes as modes +import qutebrowser.commands.utils as cmdutils from qutebrowser.keyinput.normalmode import STARTCHARS from qutebrowser.config.style import set_register_stylesheet, get_stylesheet from qutebrowser.utils.url import urlstring @@ -214,7 +215,6 @@ class _Command(QLineEdit): Attributes: history: The command history object. _statusbar: The statusbar (parent) QWidget. - _shortcuts: Defined QShortcuts to prevent GCing. _validator: The current command validator. Signals: @@ -224,9 +224,6 @@ class _Command(QLineEdit): arg: The search term. got_rev_search: Emitted when the user started a new reverse search. arg: The search term. - esc_pressed: Emitted when the escape key was pressed. - tab_pressed: Emitted when the tab key was pressed. - arg: Whether shift has been pressed. clear_completion_selection: Emitted before the completion widget is hidden. hide_completion: Emitted when the completion widget should be hidden. @@ -237,8 +234,6 @@ class _Command(QLineEdit): got_cmd = pyqtSignal(str) got_search = pyqtSignal(str) got_search_rev = pyqtSignal(str) - esc_pressed = pyqtSignal() - tab_pressed = pyqtSignal(bool) clear_completion_selection = pyqtSignal() hide_completion = pyqtSignal() show_cmd = pyqtSignal() @@ -264,44 +259,6 @@ class _Command(QLineEdit): self.returnPressed.connect(self._on_return_pressed) self.textEdited.connect(self.history.stop) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) - self._shortcuts = [] - for (key, handler) in [ - (Qt.Key_Escape, self.esc_pressed), - (Qt.Key_Up, self._on_key_up_pressed), - (Qt.Key_Down, self._on_key_down_pressed), - (Qt.Key_Tab | Qt.SHIFT, lambda: self.tab_pressed.emit(True)), - (Qt.Key_Tab, lambda: self.tab_pressed.emit(False)) - ]: - sc = QShortcut(self) - sc.setKey(QKeySequence(key)) - sc.setContext(Qt.WidgetWithChildrenShortcut) - sc.activated.connect(handler) - self._shortcuts.append(sc) - - @pyqtSlot() - def _on_key_up_pressed(self): - """Handle Up presses (go back in history).""" - try: - if not self.history.browsing: - item = self.history.start(self.text().strip()) - else: - item = self.history.previtem() - except (HistoryEmptyError, HistoryEndReachedError): - return - if item: - self.set_cmd_text(item) - - @pyqtSlot() - def _on_key_down_pressed(self): - """Handle Down presses (go forward in history).""" - if not self.history.browsing: - return - try: - item = self.history.nextitem() - except HistoryEndReachedError: - return - if item: - self.set_cmd_text(item) @pyqtSlot() def _on_return_pressed(self): @@ -355,6 +312,31 @@ class _Command(QLineEdit): self.setFocus() self.show_cmd.emit() + @cmdutils.register(instance='mainwindow.status.cmd', hide=True) + def command_history_prev(self): + """Handle Up presses (go back in history).""" + try: + if not self.history.browsing: + item = self.history.start(self.text().strip()) + else: + item = self.history.previtem() + except (HistoryEmptyError, HistoryEndReachedError): + return + if item: + self.set_cmd_text(item) + + @cmdutils.register(instance='mainwindow.status.cmd', hide=True) + def command_history_next(self): + """Handle Down presses (go forward in history).""" + if not self.history.browsing: + return + try: + item = self.history.nextitem() + except HistoryEndReachedError: + return + if item: + self.set_cmd_text(item) + def focusInEvent(self, e): """Extend focusInEvent to enter command mode.""" modes.enter("command") @@ -368,6 +350,8 @@ class _Command(QLineEdit): - Clear completion selection - Hide completion + FIXME we should rather do this on on_mode_left... + Args: e: The QFocusEvent. @@ -375,7 +359,7 @@ class _Command(QLineEdit): clear_completion_selection: Always emitted. hide_completion: Always emitted so the completion is hidden. """ - modes.leave("command") + modes.maybe_leave("command") if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason, Qt.BacktabFocusReason, Qt.OtherFocusReason]: self.setText('') diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index f8c926bca..511c76121 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -343,6 +343,12 @@ class TabbedBrowser(TabWidget): else: logging.debug('ignoring title change') + @pyqtSlot(str) + def on_mode_left(self, mode): + """Focus tabs if command mode was left.""" + if mode == "command": + self.setFocus() + def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards.