diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 99c26b1d9..fb2ce94ef 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -671,7 +671,7 @@ class HintManager(QObject): raise cmdexc.CommandError("No frame focused!") mode_manager = objreg.get('mode-manager', scope='window', window=self._win_id) - if usertypes.KeyMode.hint in mode_manager.mode_stack: + if mode_manager.mode == usertypes.KeyMode.hint: raise cmdexc.CommandError("Already hinting!") self._check_args(target, *args) self._context = HintContext() @@ -689,11 +689,8 @@ class HintManager(QObject): window=self._win_id) message_bridge.set_text(self.HINT_TEXTS[target]) self._connect_frame_signals() - try: - modeman.enter(self._win_id, usertypes.KeyMode.hint, - 'HintManager.start') - except modeman.ModeLockedError: - self._cleanup() + modeman.enter(self._win_id, usertypes.KeyMode.hint, + 'HintManager.start') def handle_partial_key(self, keystr): """Handle a new partial keypress.""" diff --git a/qutebrowser/browser/webview.py b/qutebrowser/browser/webview.py index 0280a305f..42ca7a8f7 100644 --- a/qutebrowser/browser/webview.py +++ b/qutebrowser/browser/webview.py @@ -119,6 +119,10 @@ class WebView(QWebView): hintmanager.mouse_event.connect(self.on_mouse_event) hintmanager.set_open_target.connect(self.set_force_open_target) objreg.register('hintmanager', hintmanager, registry=self.registry) + mode_manager = objreg.get('mode-manager', scope='window', + window=win_id) + mode_manager.entered.connect(self.on_mode_entered) + mode_manager.left.connect(self.on_mode_left) page.linkHovered.connect(self.linkHovered) page.mainFrame().loadStarted.connect(self.on_load_started) self.urlChanged.connect(self.on_url_changed) @@ -235,8 +239,8 @@ class WebView(QWebView): if ((hitresult.isContentEditable() and elem.is_writable()) or elem.is_editable()): log.mouse.debug("Clicked editable element!") - modeman.maybe_enter(self._win_id, usertypes.KeyMode.insert, - 'click') + modeman.enter(self._win_id, usertypes.KeyMode.insert, 'click', + only_if_normal=True) else: log.mouse.debug("Clicked non-editable element!") if config.get('input', 'auto-leave-insert-mode'): @@ -255,8 +259,8 @@ class WebView(QWebView): return if elem.is_editable(): log.mouse.debug("Clicked editable element (delayed)!") - modeman.maybe_enter(self._win_id, usertypes.KeyMode.insert, - 'click-delayed') + modeman.enter(self._win_id, usertypes.KeyMode.insert, + 'click-delayed', only_if_normal=True) else: log.mouse.debug("Clicked non-editable element (delayed)!") if config.get('input', 'auto-leave-insert-mode'): @@ -397,7 +401,7 @@ class WebView(QWebView): return mode_manager = objreg.get('mode-manager', scope='window', window=self._win_id) - cur_mode = mode_manager.mode() + cur_mode = mode_manager.mode if cur_mode == usertypes.KeyMode.insert or not ok: return frame = self.page().currentFrame() @@ -408,8 +412,26 @@ class WebView(QWebView): return log.modes.debug("focus element: {}".format(repr(elem))) if elem.is_editable(): - modeman.maybe_enter(self._win_id, usertypes.KeyMode.insert, - 'load finished') + modeman.enter(self._win_id, usertypes.KeyMode.insert, + 'load finished', only_if_normal=True) + + @pyqtSlot(usertypes.KeyMode) + def on_mode_entered(self, mode): + """Ignore attempts to focus the widget if in any status-input mode.""" + if mode in (usertypes.KeyMode.command, usertypes.KeyMode.prompt, + usertypes.KeyMode.yesno): + log.webview.debug("Ignoring focus because mode {} was " + "entered.".format(mode)) + self.setFocusPolicy(Qt.NoFocus) + + @pyqtSlot(usertypes.KeyMode) + def on_mode_left(self, mode): + """Restore focus policy if status-input modes were left.""" + if mode in (usertypes.KeyMode.command, usertypes.KeyMode.prompt, + usertypes.KeyMode.yesno): + log.webview.debug("Restoring focus policy because mode {} was " + "left.".format(mode)) + self.setFocusPolicy(Qt.WheelFocus) @pyqtSlot(str) def set_force_open_target(self, target): diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index 6c7447a1e..e71474fbd 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -106,7 +106,7 @@ class Command: """ mode_manager = objreg.get('mode-manager', scope='window', window=win_id) - curmode = mode_manager.mode() + curmode = mode_manager.mode if self._modes is not None and curmode not in self._modes: mode_names = '/'.join(mode.name for mode in self._modes) raise cmdexc.PrerequisitesError( diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 559a92531..0194e2f60 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -35,11 +35,6 @@ from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.utils import usertypes, log, objreg, utils -class ModeLockedError(Exception): - - """Exception raised when the mode is currently locked.""" - - class NotInModeError(Exception): """Exception raised when we want to leave a mode we're not in.""" @@ -82,9 +77,9 @@ def _get_modeman(win_id): return objreg.get('mode-manager', scope='window', window=win_id) -def enter(win_id, mode, reason=None, override=False): +def enter(win_id, mode, reason=None, only_if_normal=False): """Enter the mode 'mode'.""" - _get_modeman(win_id).enter(mode, reason, override) + _get_modeman(win_id).enter(mode, reason, only_if_normal) def leave(win_id, mode, reason=None): @@ -92,14 +87,6 @@ def leave(win_id, mode, reason=None): _get_modeman(win_id).leave(mode, reason) -def maybe_enter(win_id, mode, reason=None, override=False): - """Convenience method to enter 'mode' without exceptions.""" - try: - _get_modeman(win_id).enter(mode, reason, override) - except ModeLockedError: - pass - - def maybe_leave(win_id, mode, reason=None): """Convenience method to leave 'mode' without exceptions.""" try: @@ -141,11 +128,7 @@ class ModeManager(QObject): Attributes: passthrough: A list of modes in which to pass through events. - locked: Whether current mode is locked. This means the current mode can - still be left (then locked will be reset), but no new mode can - be entered while the current mode is active. - mode_stack: A list of the modes we're currently in, with the active - one on the right. + mode: The mode we're currently in. _win_id: The window ID of this ModeManager _handlers: A dictionary of modes and their handlers. _forward_unbound_keys: If we should forward unbound keys. @@ -169,25 +152,18 @@ class ModeManager(QObject): def __init__(self, win_id, parent=None): super().__init__(parent) self._win_id = win_id - self.locked = False self._handlers = {} self.passthrough = [] - self.mode_stack = [] + self.mode = usertypes.KeyMode.none self._releaseevents_to_pass = [] self._forward_unbound_keys = config.get( 'input', 'forward-unbound-keys') objreg.get('config').changed.connect(self.set_forward_unbound_keys) def __repr__(self): - return utils.get_repr(self, mode=self.mode(), locked=self.locked, + return utils.get_repr(self, mode=self.mode, passthrough=self.passthrough) - def mode(self): - """Get the current mode..""" - if not self.mode_stack: - return None - return self.mode_stack[-1] - def _eventFilter_keypress(self, event): """Handle filtering of KeyPress events. @@ -197,7 +173,7 @@ class ModeManager(QObject): Return: True if event should be filtered, False otherwise. """ - curmode = self.mode() + curmode = self.mode handler = self._handlers[curmode] if curmode != usertypes.KeyMode.insert: log.modes.debug("got keypress in mode {} - calling handler " @@ -245,7 +221,7 @@ class ModeManager(QObject): filter_this = False else: filter_this = True - if self.mode() != usertypes.KeyMode.insert: + if self.mode != usertypes.KeyMode.insert: log.modes.debug("filter: {}".format(filter_this)) return filter_this @@ -264,31 +240,33 @@ class ModeManager(QObject): if passthrough: self.passthrough.append(mode) - def enter(self, mode, reason=None, override=False): + def enter(self, mode, reason=None, only_if_normal=False): """Enter a new mode. Args: mode: The mode to enter as a KeyMode member. reason: Why the mode was entered. - override: Override a locked mode. + only_if_normal: Only enter the new mode if we're in normal mode. """ if not isinstance(mode, usertypes.KeyMode): raise TypeError("Mode {} is no KeyMode member!".format(mode)) - if self.locked: - if override: - log.modes.debug("Locked to mode {}, but overriding to " - "{}.".format(self.mode(), mode)) - else: - log.modes.debug("Not entering mode {} because mode is locked " - "to {}.".format(mode, self.mode())) - raise ModeLockedError("Mode is currently locked to {}".format( - self.mode())) log.modes.debug("Entering mode {}{}".format( mode, '' if reason is None else ' (reason: {})'.format(reason))) if mode not in self._handlers: raise ValueError("No handler for mode {}".format(mode)) - self.mode_stack.append(mode) - log.modes.debug("New mode stack: {}".format(self.mode_stack)) + if self.mode == mode: + log.modes.debug("Ignoring request as we're in mode {} " + "already.".format(self.mode)) + return + if self.mode != usertypes.KeyMode.normal: + if only_if_normal: + log.modes.debug("Ignoring request as we're in mode {} " + "and only_if_normal is set..".format( + self.mode)) + return + log.modes.debug("Overriding mode {}.".format(self.mode)) + self.left.emit(self.mode, mode, self._win_id) + self.mode = mode self.entered.emit(mode, self._win_id) @cmdutils.register(instance='mode-manager', hide=True, scope='window') @@ -311,24 +289,21 @@ class ModeManager(QObject): mode: The name of the mode to leave. reason: Why the mode was left. """ - try: - self.mode_stack.remove(mode) - except ValueError: - raise NotInModeError("Mode {} not on mode stack!".format(mode)) - self.locked = False - log.modes.debug("Leaving mode {}{}, new mode stack {}".format( - mode, '' if reason is None else ' (reason: {})'.format(reason), - self.mode_stack)) - self.left.emit(mode, self.mode(), self._win_id) + if self.mode != mode: + raise NotInModeError("Not in mode {}!".format(mode)) + log.modes.debug("Leaving mode {}{}".format( + mode, '' if reason is None else ' (reason: {})'.format(reason))) + self.mode = usertypes.KeyMode.normal + self.left.emit(mode, self.mode, self._win_id) @cmdutils.register(instance='mode-manager', name='leave-mode', not_modes=[usertypes.KeyMode.normal], hide=True, scope='window') def leave_current_mode(self): """Leave the mode we're currently in.""" - if self.mode() == usertypes.KeyMode.normal: + if self.mode == usertypes.KeyMode.normal: raise ValueError("Can't leave normal mode!") - self.leave(self.mode(), 'leave current') + self.leave(self.mode, 'leave current') @config.change_filter('input', 'forward-unbound-keys') def set_forward_unbound_keys(self): @@ -347,7 +322,7 @@ class ModeManager(QObject): Return: True if event should be filtered, False otherwise. """ - if self.mode() is None: + if self.mode is None: # We got events before mode is set, so just pass them through. return False typ = event.type() diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py index 005e7b203..26b4420b4 100644 --- a/qutebrowser/mainwindow/statusbar/command.py +++ b/qutebrowser/mainwindow/statusbar/command.py @@ -90,6 +90,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): """ self.setText(text) log.modes.debug("Setting command text, focusing {!r}".format(self)) + modeman.enter(self._win_id, usertypes.KeyMode.command, 'cmd focus') self.setFocus() self.show_cmd.emit() @@ -183,12 +184,6 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): self.clear_completion_selection.emit() self.hide_completion.emit() - def focusInEvent(self, e): - """Extend focusInEvent to enter command mode.""" - modeman.maybe_enter(self._win_id, usertypes.KeyMode.command, - 'cmd focus') - super().focusInEvent(e) - def setText(self, text): """Extend setText to set prefix and make sure the prompt is ok.""" if not text: diff --git a/qutebrowser/mainwindow/statusbar/prompter.py b/qutebrowser/mainwindow/statusbar/prompter.py index ec6d2b2f2..fd470ea3d 100644 --- a/qutebrowser/mainwindow/statusbar/prompter.py +++ b/qutebrowser/mainwindow/statusbar/prompter.py @@ -320,15 +320,7 @@ class Prompter(QObject): mode = self._display_question() question.aborted.connect( lambda: modeman.maybe_leave(self._win_id, mode, 'aborted')) - mode_manager = objreg.get('mode-manager', scope='window', - window=self._win_id) - try: - modeman.enter(self._win_id, mode, 'question asked', override=True) - except modeman.ModeLockedError: - if mode_manager.mode() != usertypes.KeyMode.prompt: - question.abort() - return None - mode_manager.locked = True + modeman.enter(self._win_id, mode, 'question asked') if blocking: loop = qtutils.EventLoop() self._loops.append(loop) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 0dd63fc4d..aa9f7d6b7 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -230,8 +230,8 @@ ClickTarget = enum('ClickTarget', ['normal', 'tab', 'tab_bg', 'window']) # Key input modes -KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt', - 'insert', 'passthrough']) +KeyMode = enum('KeyMode', ['none', 'normal', 'hint', 'command', 'yesno', + 'prompt', 'insert', 'passthrough']) # Available command completions