diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 6e04ebe51..ffe780333 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -30,6 +30,9 @@ from qutebrowser.config import config from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.utils import usertypes, log, objreg, utils +INPUT_MODES = [usertypes.KeyMode.insert, usertypes.KeyMode.passthrough] +PROMPT_MODES = [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno] + @attr.s(frozen=True) class KeyEvent: @@ -121,6 +124,7 @@ class ModeManager(QObject): Attributes: mode: The mode we're currently in. _win_id: The window ID of this ModeManager + _prev_mode: Mode before a prompt popped up _parsers: A dictionary of modes and their keyparsers. _forward_unbound_keys: If we should forward unbound keys. _releaseevents_to_pass: A set of KeyEvents where the keyPressEvent was @@ -144,6 +148,7 @@ class ModeManager(QObject): super().__init__(parent) self._win_id = win_id self._parsers = {} + self._prev_mode = usertypes.KeyMode.normal self.mode = usertypes.KeyMode.normal self._releaseevents_to_pass = set() @@ -240,9 +245,8 @@ class ModeManager(QObject): mode, '' if reason is None else ' (reason: {})'.format(reason))) if mode not in self._parsers: raise ValueError("No keyparser for mode {}".format(mode)) - prompt_modes = (usertypes.KeyMode.prompt, usertypes.KeyMode.yesno) - if self.mode == mode or (self.mode in prompt_modes and - mode in prompt_modes): + if self.mode == mode or (self.mode in PROMPT_MODES and + mode in PROMPT_MODES): log.modes.debug("Ignoring request as we're in mode {} " "already.".format(self.mode)) return @@ -254,6 +258,11 @@ class ModeManager(QObject): return log.modes.debug("Overriding mode {}.".format(self.mode)) self.left.emit(self.mode, mode, self._win_id) + if (mode in PROMPT_MODES and self.mode in INPUT_MODES and + config.val.tabs.mode_on_change == 'restore'): + self._prev_mode = self.mode + else: + self._prev_mode = usertypes.KeyMode.normal self.mode = mode self.entered.emit(mode, self._win_id) @@ -301,6 +310,9 @@ class ModeManager(QObject): self.clear_keychain() self.mode = usertypes.KeyMode.normal self.left.emit(mode, self.mode, self._win_id) + if mode in PROMPT_MODES: + self.enter(self._prev_mode, + reason='restore mode before {}'.format(mode.name)) @cmdutils.register(instance='mode-manager', name='leave-mode', not_modes=[usertypes.KeyMode.normal], scope='window') diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 0f66d6797..4cfee7eff 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -492,6 +492,7 @@ class MainWindow(QWidget): self.tabbed_browser.cur_fullscreen_requested.connect(status.maybe_hide) # command input / completion + mode_manager.entered.connect(self.tabbed_browser.on_mode_entered) mode_manager.left.connect(self.tabbed_browser.on_mode_left) cmd.clear_completion_selection.connect( completion_obj.on_clear_completion_selection) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index aa5f74824..2b74df72b 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -644,24 +644,32 @@ class TabbedBrowser(QWidget): if config.val.tabs.tabs_are_windows: self.widget.window().setWindowIcon(icon) + @pyqtSlot(usertypes.KeyMode) + def on_mode_entered(self, mode): + """Save input mode when tabs.mode_on_change = restore.""" + if (config.val.tabs.mode_on_change == 'restore' and + mode in modeman.INPUT_MODES): + tab = self.widget.currentWidget() + if tab is not None: + tab.data.input_mode = mode + @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Give focus to current tab if command mode was left.""" - if mode in [usertypes.KeyMode.command, usertypes.KeyMode.prompt, - usertypes.KeyMode.yesno]: - widget = self.widget.currentWidget() + widget = self.widget.currentWidget() + if widget is None: + return + if mode in [usertypes.KeyMode.command] + modeman.PROMPT_MODES: log.modes.debug("Left status-input mode, focusing {!r}".format( widget)) - if widget is None: - return widget.setFocus() + if config.val.tabs.mode_on_change == 'restore': + widget.data.input_mode = usertypes.KeyMode.normal @pyqtSlot(int) def on_current_changed(self, idx): """Set last-focused-tab and leave hinting mode when focus changed.""" mode_on_change = config.val.tabs.mode_on_change - modes_to_save = [usertypes.KeyMode.insert, - usertypes.KeyMode.passthrough] if idx == -1 or self.shutting_down: # closing the last tab (before quitting) or shutting down return @@ -670,26 +678,28 @@ class TabbedBrowser(QWidget): log.webview.debug("on_current_changed got called with invalid " "index {}".format(idx)) return - if self._now_focused is not None and mode_on_change == 'restore': - current_mode = modeman.instance(self._win_id).mode - if current_mode not in modes_to_save: - current_mode = usertypes.KeyMode.normal - self._now_focused.data.input_mode = current_mode log.modes.debug("Current tab changed, focusing {!r}".format(tab)) tab.setFocus() modes_to_leave = [usertypes.KeyMode.hint, usertypes.KeyMode.caret] - if mode_on_change != 'persist': - modes_to_leave += modes_to_save + + mm_instance = modeman.instance(self._win_id) + current_mode = mm_instance.mode + log.modes.debug("Mode before tab change: {} (mode_on_change = {})" + .format(current_mode.name, mode_on_change)) + if mode_on_change == 'normal': + modes_to_leave += modeman.INPUT_MODES for mode in modes_to_leave: modeman.leave(self._win_id, mode, 'tab changed', maybe=True) - if mode_on_change == 'restore': - modeman.enter(self._win_id, tab.data.input_mode, - 'restore input mode for tab') + if (mode_on_change == 'restore' and + current_mode not in modeman.PROMPT_MODES): + modeman.enter(self._win_id, tab.data.input_mode, 'restore') if self._now_focused is not None: objreg.register('last-focused-tab', self._now_focused, update=True, scope='window', window=self._win_id) + log.modes.debug("Mode after tab change: {} (mode_on_change = {})" + .format(current_mode.name, mode_on_change)) self._now_focused = tab self.current_tab_changed.emit(tab) QTimer.singleShot(0, self._update_window_title) diff --git a/tests/end2end/features/keyinput.feature b/tests/end2end/features/keyinput.feature index b5ab7fcc1..6dc7fa247 100644 --- a/tests/end2end/features/keyinput.feature +++ b/tests/end2end/features/keyinput.feature @@ -160,3 +160,45 @@ Feature: Keyboard input And I press the key "c" Then the message "foo 3" should be shown And the message "foo 3" should be shown + + # test all tabs.mode_on_change modes + + Scenario: mode on change normal + Given I set tabs.mode_on_change to normal + And I clean up open tabs + When I open about:blank + And I run :enter-mode insert + And I open about:blank in a new tab + Then "Entering mode KeyMode.insert (reason: command)" should be logged + And "Leaving mode KeyMode.insert (reason: tab changed)" should be logged + And "Mode before tab change: insert (mode_on_change = normal)" should be logged + And "Mode after tab change: normal (mode_on_change = normal)" should be logged + + Scenario: mode on change persist + Given I set tabs.mode_on_change to persist + And I clean up open tabs + When I open about:blank + And I run :enter-mode insert + And I open about:blank in a new tab + Then "Entering mode KeyMode.insert (reason: command)" should be logged + And "Leaving mode KeyMode.insert (reason: tab changed)" should not be logged + And "Mode before tab change: insert (mode_on_change = persist)" should be logged + And "Mode after tab change: insert (mode_on_change = persist)" should be logged + + Scenario: mode on change restore + Given I set tabs.mode_on_change to restore + And I clean up open tabs + When I open about:blank + And I run :enter-mode insert + And I open about:blank in a new tab + And I run :enter-mode passthrough + And I run :tab-focus 1 + Then "Entering mode KeyMode.insert (reason: command)" should be logged + And "Mode before tab change: insert (mode_on_change = restore)" should be logged + And "Entering mode KeyMode.normal (reason: restore)" should be logged + And "Mode after tab change: normal (mode_on_change = restore)" should be logged + And "Entering mode KeyMode.passthrough (reason: command)" should be logged + And "Mode before tab change: passthrough (mode_on_change = restore)" should be logged + And "Entering mode KeyMode.insert (reason: restore)" should be logged + And "Mode after tab change: insert (mode_on_change = restore)" should be logged +