First try at statusbar prompt

This commit is contained in:
Florian Bruhin 2014-05-19 17:01:05 +02:00
parent 9ce06c75bc
commit 7ca605ade6
5 changed files with 204 additions and 4 deletions

View File

@ -212,6 +212,7 @@ class QuteBrowser(QApplication):
'insert': PassthroughKeyParser('keybind.insert', self),
'passthrough': PassthroughKeyParser('keybind.passthrough', self),
'command': PassthroughKeyParser('keybind.command', self),
'prompt': PassthroughKeyParser('keybind.prompt', self),
}
self.modeman = ModeManager()
self.modeman.register('normal', self._keyparsers['normal'].handle)
@ -223,6 +224,8 @@ class QuteBrowser(QApplication):
passthrough=True)
self.modeman.register('command', self._keyparsers['command'].handle,
passthrough=True)
self.modeman.register('prompt', self._keyparsers['prompt'].handle,
passthrough=True)
def _init_log(self):
"""Initialisation of the logging output.
@ -363,6 +366,8 @@ class QuteBrowser(QApplication):
self.lastWindowClosed.connect(self.shutdown)
tabs.quit.connect(self.shutdown)
tabs.currentChanged.connect(self.mainwindow.update_inspector)
self.networkmanager.authenticationRequired.connect(
status.on_authentication_required)
# status bar
self.modeman.entered.connect(status.on_mode_entered)

View File

@ -119,6 +119,11 @@ SECTION_DESC = {
" completion-item-next: Select next item in completion.\n"
" command-accept: Execute the command currently in the commandline.\n"
" leave-mode: Leave the command mode."),
'keybind.prompt': (
"Keybindings for prompts in the status line.\n"
"Useful hidden commands to map in this section:\n"
" prompt-accept: Confirm the entered value\n"
" leave-mode: Leave the prompt mode."),
'aliases': (
"Aliases for commands.\n"
"By default, no aliases are defined. Example which adds a new command "
@ -691,6 +696,13 @@ DATA = OrderedDict([
('<Return>', 'command-accept'),
)),
('keybind.prompt', sect.ValueList(
types.KeyBindingName(), types.KeyBinding(),
#('<Escape>', 'leave-mode'),
#('<Ctrl-N>', 'leave-mode'),
('<Return>', 'prompt-accept'),
)),
('aliases', sect.ValueList(
types.String(forbidden=' '), types.Command(),
)),

View File

@ -0,0 +1,147 @@
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Prompt shown in the statusbar."""
from PyQt5.QtCore import pyqtSignal, QEventLoop
from PyQt5.QtWidgets import QLineEdit, QHBoxLayout
import qutebrowser.keyinput.modeman as modeman
import qutebrowser.commands.utils as cmdutils
from qutebrowser.widgets.statusbar._textbase import TextBase
from qutebrowser.utils.usertypes import enum
PromptMode = enum('yesno', 'text', 'user_pwd')
class Prompt(TextBase):
answered = pyqtSignal([str], [bool], [str, str])
accepted = pyqtSignal()
hide_prompt = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.mode = None
self.default = None
self.text = None
self.answer = None
self.loop = QEventLoop()
self._hbox = QHBoxLayout(self)
self._hbox.setContentsMargins(0, 0, 0, 0)
self._hbox.setSpacing(5)
self._txt = TextBase()
self._hbox.addWidget(self._txt)
self._input = _QueryInput()
self._hbox.addWidget(self._input)
def _user_entered(self):
self._user = self._input.text()
self._txt.setText("Password:")
self._input.clear()
self._input.setEchoMode(QLineEdit.Password)
self.accepted.disconnect(self._user_entered)
self.accepted.connect(self._password_entered)
def _password_entered(self):
self.accepted.disconnect(self._password_entered)
password = self._input.text()
self.answer = (self._user, password)
self._txt.setText('')
self._input.clear()
self._input.setEchoMode(QLineEdit.Normal)
self.default = None
self.mode = None
self.text = None
self.answered[str, str].emit(*self.answer)
modeman.leave('prompt', 'prompt accept')
self.hide_prompt.emit()
def on_return_pressed(self):
self.accepted.disconnect(self.on_return_pressed)
self.answer = self._input.text()
self._txt.setText('')
self.default = None
self.mode = None
self.text = None
# FIXME handle bool correctly
self.answered[str].emit(self.answer)
modeman.leave('prompt', 'prompt accept')
self.hide_prompt.emit()
@cmdutils.register(instance='mainwindow.status.prompt', hide=True,
modes=['prompt'])
def prompt_accept(self):
"""Accept the prompt. """
self.accepted.emit()
def display(self):
if self.mode == PromptMode.yesno:
if self.default is None:
suffix = " [y/n]"
elif self.default:
suffix = " [Y/n]"
else:
suffix = " [y/N]"
self._txt.setText(self.text + suffix)
self._input.hide()
elif self.mode == PromptMode.text:
self._txt.setText(self.text)
if self.default:
self._input.setText(self.default)
self._input.show()
self.accepted.connect(self.on_return_pressed)
elif self.mode == PromptMode.user_pwd:
self._txt.setText(self.text)
if self.default:
self._input.setText(self.default)
self._input.show()
self.accepted.connect(self._user_entered)
else:
raise ValueError("Invalid prompt mode!")
self._input.setFocus()
def exec_(self):
self.display()
self.answered[str, str].connect(self.loop.quit)
self.loop.exec_()
return self.answer
class _QueryInput(QLineEdit):
"""Minimal QLineEdit used for input."""
def __init__(self, parent=None):
super().__init__(parent)
self.setStyleSheet("""
QLineEdit {
border: 0px;
padding-left: 1px;
background-color: transparent;
}
""")
def focusInEvent(self, e):
"""Extend focusInEvent to enter command mode."""
modeman.enter('prompt', 'auth focus')
super().focusInEvent(e)

View File

@ -36,8 +36,8 @@ class TextBase(QLabel):
_elided_text: The current elided text.
"""
def __init__(self, bar, elidemode=Qt.ElideRight):
super().__init__(bar)
def __init__(self, parent=None, elidemode=Qt.ElideRight):
super().__init__(parent)
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
self._elidemode = elidemode
self._elided_text = ''
@ -48,8 +48,9 @@ class TextBase(QLabel):
Args:
width: The maximal width the text should take.
"""
self._elided_text = self.fontMetrics().elidedText(
self.text(), self._elidemode, width, Qt.TextShowMnemonic)
if self.text is not None:
self._elided_text = self.fontMetrics().elidedText(
self.text(), self._elidemode, width, Qt.TextShowMnemonic)
def setText(self, txt):
"""Extend QLabel::setText.

View File

@ -32,6 +32,7 @@ from qutebrowser.widgets.statusbar._text import Text
from qutebrowser.widgets.statusbar._keystring import KeyString
from qutebrowser.widgets.statusbar._percentage import Percentage
from qutebrowser.widgets.statusbar._url import Url
from qutebrowser.widgets.statusbar._prompt import Prompt, PromptMode
from qutebrowser.config.style import set_register_stylesheet, get_stylesheet
@ -122,9 +123,14 @@ class StatusBar(QWidget):
self._text_pop_timer.setInterval(config.get('ui', 'message-timeout'))
self._text_pop_timer.timeout.connect(self._pop_text)
self.prompt = Prompt(self)
self._stack.addWidget(self.prompt)
self.cmd.show_cmd.connect(self._show_cmd_widget)
self.cmd.hide_cmd.connect(self._hide_cmd_widget)
self._hide_cmd_widget()
self.prompt.hide_prompt.connect(self._hide_prompt_widget)
self._hide_prompt_widget()
self._hbox.addLayout(self._stack)
@ -189,6 +195,24 @@ class StatusBar(QWidget):
self._timer_was_active = False
self._stack.setCurrentWidget(self.txt)
def _show_prompt_widget(self):
"""Show prompt widget instead of temporary text."""
self.error = False
if self._text_pop_timer.isActive():
self._timer_was_active = True
self._text_pop_timer.stop()
self._stack.setCurrentWidget(self.prompt)
def _hide_prompt_widget(self):
"""Show temporary text instead of prompt widget."""
logging.debug("Hiding prompt widget, queue: {}".format(self._text_queue))
if self._timer_was_active:
# Restart the text pop timer if it was active before hiding.
self._pop_text()
self._text_pop_timer.start()
self._timer_was_active = False
self._stack.setCurrentWidget(self.txt)
def _disp_text(self, text, error, queue=False):
"""Inner logic for disp_error and disp_temp_text.
@ -292,6 +316,17 @@ class StatusBar(QWidget):
self._text_pop_timer.setInterval(config.get('ui',
'message-timeout'))
@pyqtSlot('QNetworkReply', 'QAuthenticator')
def on_authentication_required(self, reply, authenticator):
self._show_prompt_widget()
self.prompt.mode = PromptMode.user_pwd
self.prompt.text = "Username ({}):".format(authenticator.realm())
user, password = self.prompt.exec_()
self._hide_prompt_widget()
authenticator.setUser(user)
authenticator.setPassword(password)
logging.debug("user: {} / password: {}".format(user, password))
def resizeEvent(self, e):
"""Extend resizeEvent of QWidget to emit a resized signal afterwards.