From cfd70e782145ec7231223180d34f58fb12844460 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 20 May 2014 12:05:14 +0200 Subject: [PATCH] Make yes/no questions work --- qutebrowser/app.py | 5 ++- qutebrowser/config/configdata.py | 9 +++- qutebrowser/keyinput/modeparsers.py | 11 +++++ qutebrowser/widgets/statusbar/_prompt.py | 57 ++++++++++++++++-------- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 5439ba81c..7bff02c99 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -55,7 +55,8 @@ from qutebrowser.config.config import ConfigManager from qutebrowser.keyinput.modeman import ModeManager from qutebrowser.widgets.mainwindow import MainWindow from qutebrowser.widgets.crash import ExceptionCrashDialog, FatalCrashDialog -from qutebrowser.keyinput.modeparsers import NormalKeyParser, HintKeyParser +from qutebrowser.keyinput.modeparsers import (NormalKeyParser, HintKeyParser, + PromptKeyParser) from qutebrowser.keyinput.keyparser import PassthroughKeyParser from qutebrowser.commands.managers import CommandManager, SearchManager from qutebrowser.commands.exceptions import CommandError @@ -212,6 +213,7 @@ class QuteBrowser(QApplication): 'passthrough': PassthroughKeyParser('keybind.passthrough', self), 'command': PassthroughKeyParser('keybind.command', self), 'prompt': PassthroughKeyParser('keybind.prompt', self), + 'yesno': PromptKeyParser(self), } self.modeman = ModeManager() self.modeman.register('normal', self._keyparsers['normal'].handle) @@ -225,6 +227,7 @@ class QuteBrowser(QApplication): passthrough=True) self.modeman.register('prompt', self._keyparsers['prompt'].handle, passthrough=True) + self.modeman.register('yesno', self._keyparsers['yesno'].handle) def _init_log(self): """Initialisation of the logging output. diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 7d6214a82..a090db6c1 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -121,8 +121,13 @@ SECTION_DESC = { " leave-mode: Leave the command mode."), 'keybind.prompt': ( "Keybindings for prompts in the status line.\n" + "You can bind normal keys in this mode, but they will be only active " + "when a yes/no-prompt is asked. For other prompt modes, you can only " + "bind special keys.\n" "Useful hidden commands to map in this section:\n" - " prompt-accept: Confirm the entered value\n" + " prompt-accept: Confirm the entered value.\n" + " prompt-yes: Answer yes to a yes/no question.\n" + " prompt-no: Answer no to a yes/no question.\n" " leave-mode: Leave the prompt mode."), 'aliases': ( "Aliases for commands.\n" @@ -701,6 +706,8 @@ DATA = OrderedDict([ ('', 'leave-mode'), ('', 'leave-mode'), ('', 'prompt-accept'), + ('y', 'prompt-yes'), + ('n', 'prompt-no'), )), ('aliases', sect.ValueList( diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 70779cf9a..ac1686919 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -59,6 +59,17 @@ class NormalKeyParser(CommandKeyParser): return super()._handle_single_key(e) +class PromptKeyParser(CommandKeyParser): + + """KeyParser for yes/no prompts.""" + + def __init__(self, parent=None): + super().__init__(parent, supports_count=False, supports_chains=True) + # We don't want an extra section for this in the config, so we just + # abuse the keybind.prompt section. + self.read_config('keybind.prompt') + + class HintKeyParser(CommandKeyParser): """KeyChainParser for hints. diff --git a/qutebrowser/widgets/statusbar/_prompt.py b/qutebrowser/widgets/statusbar/_prompt.py index df2ec11f1..78e4fdbc2 100644 --- a/qutebrowser/widgets/statusbar/_prompt.py +++ b/qutebrowser/widgets/statusbar/_prompt.py @@ -22,6 +22,7 @@ from PyQt5.QtWidgets import QHBoxLayout, QWidget, QLineEdit import qutebrowser.keyinput.modeman as modeman import qutebrowser.commands.utils as cmdutils +import qutebrowser.config.config as config from qutebrowser.widgets.statusbar._textbase import TextBase from qutebrowser.widgets.misc import MinimalLineEdit from qutebrowser.utils.usertypes import PromptMode, Question @@ -36,7 +37,7 @@ class Prompt(QWidget): loop: A local QEventLoop to spin in exec_. _hbox: The QHBoxLayout used to display the text and prompt. _txt: The TextBase instance (QLabel) used to display the prompt text. - _input: The QueryInput instance (QLineEdit) used for the input. + _input: The MinimalLineEdit instance (QLineEdit) used for the input. Signals: show_prompt: Emitted when the prompt widget wants to be shown. @@ -61,7 +62,7 @@ class Prompt(QWidget): self._txt = TextBase() self._hbox.addWidget(self._txt) - self._input = _QueryInput() + self._input = MinimalLineEdit() self._hbox.addWidget(self._input) def on_mode_left(self, mode): @@ -71,7 +72,7 @@ class Prompt(QWidget): cancelled: Emitted when the mode was forcibly left by the user without answering the question. """ - if mode == 'prompt': + if mode in ['prompt', 'yesno']: self._txt.setText('') self._input.clear() self._input.setEchoMode(QLineEdit.Normal) @@ -100,10 +101,36 @@ class Prompt(QWidget): self.question.answer = (self.question.user, password) modeman.leave('prompt', 'prompt accept') self.hide_prompt.emit() - else: - # User just entered all information needed in some other mode. + elif self.question.mode == PromptMode.text: + # User just entered text. self.question.answer = self._input.text() modeman.leave('prompt', 'prompt accept') + elif self.question.mode == PromptMode.yesno: + # User wants to accept the default of a yes/no question. + self.question.answer = self.question.default + modeman.leave('yesno', 'yesno accept') + else: + raise ValueError("Invalid question mode!") + + @cmdutils.register(instance='mainwindow.status.prompt', hide=True, + modes=['yesno']) + def prompt_yes(self): + """Answer yes to a yes/no prompt.""" + if self.question.mode != PromptMode.yesno: + # We just ignore this if we don't have a yes/no question. + return + self.question.answer = True + modeman.leave('yesno', 'yesno accept') + + @cmdutils.register(instance='mainwindow.status.prompt', hide=True, + modes=['yesno']) + def prompt_no(self): + """Answer no to a yes/no prompt.""" + if self.question.mode != PromptMode.yesno: + # We just ignore this if we don't have a yes/no question. + return + self.question.answer = False + modeman.leave('yesno', 'prompt accept') def display(self): """Display the question in self.question in the widget. @@ -114,27 +141,31 @@ class Prompt(QWidget): q = self.question if q.mode == PromptMode.yesno: if q.default is None: - suffix = " [y/n]" + suffix = "" elif q.default: - suffix = " [Y/n]" + suffix = " (yes)" else: - suffix = " [y/N]" + suffix = " (no)" self._txt.setText(q.text + suffix) self._input.hide() + mode = 'yesno' elif q.mode == PromptMode.text: self._txt.setText(q.text) if q.default: self._input.setText(q.default) self._input.show() + mode = 'prompt' elif q.mode == PromptMode.user_pwd: self._txt.setText(q.text) if q.default: self._input.setText(q.default) self._input.show() + mode = 'prompt' else: raise ValueError("Invalid prompt mode!") self._input.setFocus() self.show_prompt.emit() + modeman.enter(mode, 'question asked') @pyqtSlot(Question, bool) def ask_question(self, question, blocking): @@ -163,13 +194,3 @@ class Prompt(QWidget): self.cancelled.connect(self.loop.quit) self.loop.exec_() return self.question.answer - - -class _QueryInput(MinimalLineEdit): - - """QLineEdit used for input.""" - - def focusInEvent(self, e): - """Extend focusInEvent to enter command mode.""" - modeman.enter('prompt', 'auth focus') - super().focusInEvent(e)