First try at statusbar prompt
This commit is contained in:
parent
9ce06c75bc
commit
7ca605ade6
@ -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)
|
||||
|
@ -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(),
|
||||
)),
|
||||
|
147
qutebrowser/widgets/statusbar/_prompt.py
Normal file
147
qutebrowser/widgets/statusbar/_prompt.py
Normal 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)
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user