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),
|
'insert': PassthroughKeyParser('keybind.insert', self),
|
||||||
'passthrough': PassthroughKeyParser('keybind.passthrough', self),
|
'passthrough': PassthroughKeyParser('keybind.passthrough', self),
|
||||||
'command': PassthroughKeyParser('keybind.command', self),
|
'command': PassthroughKeyParser('keybind.command', self),
|
||||||
|
'prompt': PassthroughKeyParser('keybind.prompt', self),
|
||||||
}
|
}
|
||||||
self.modeman = ModeManager()
|
self.modeman = ModeManager()
|
||||||
self.modeman.register('normal', self._keyparsers['normal'].handle)
|
self.modeman.register('normal', self._keyparsers['normal'].handle)
|
||||||
@ -223,6 +224,8 @@ class QuteBrowser(QApplication):
|
|||||||
passthrough=True)
|
passthrough=True)
|
||||||
self.modeman.register('command', self._keyparsers['command'].handle,
|
self.modeman.register('command', self._keyparsers['command'].handle,
|
||||||
passthrough=True)
|
passthrough=True)
|
||||||
|
self.modeman.register('prompt', self._keyparsers['prompt'].handle,
|
||||||
|
passthrough=True)
|
||||||
|
|
||||||
def _init_log(self):
|
def _init_log(self):
|
||||||
"""Initialisation of the logging output.
|
"""Initialisation of the logging output.
|
||||||
@ -363,6 +366,8 @@ class QuteBrowser(QApplication):
|
|||||||
self.lastWindowClosed.connect(self.shutdown)
|
self.lastWindowClosed.connect(self.shutdown)
|
||||||
tabs.quit.connect(self.shutdown)
|
tabs.quit.connect(self.shutdown)
|
||||||
tabs.currentChanged.connect(self.mainwindow.update_inspector)
|
tabs.currentChanged.connect(self.mainwindow.update_inspector)
|
||||||
|
self.networkmanager.authenticationRequired.connect(
|
||||||
|
status.on_authentication_required)
|
||||||
|
|
||||||
# status bar
|
# status bar
|
||||||
self.modeman.entered.connect(status.on_mode_entered)
|
self.modeman.entered.connect(status.on_mode_entered)
|
||||||
|
@ -119,6 +119,11 @@ SECTION_DESC = {
|
|||||||
" completion-item-next: Select next item in completion.\n"
|
" completion-item-next: Select next item in completion.\n"
|
||||||
" command-accept: Execute the command currently in the commandline.\n"
|
" command-accept: Execute the command currently in the commandline.\n"
|
||||||
" leave-mode: Leave the command mode."),
|
" 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': (
|
||||||
"Aliases for commands.\n"
|
"Aliases for commands.\n"
|
||||||
"By default, no aliases are defined. Example which adds a new command "
|
"By default, no aliases are defined. Example which adds a new command "
|
||||||
@ -691,6 +696,13 @@ DATA = OrderedDict([
|
|||||||
('<Return>', 'command-accept'),
|
('<Return>', 'command-accept'),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
|
('keybind.prompt', sect.ValueList(
|
||||||
|
types.KeyBindingName(), types.KeyBinding(),
|
||||||
|
#('<Escape>', 'leave-mode'),
|
||||||
|
#('<Ctrl-N>', 'leave-mode'),
|
||||||
|
('<Return>', 'prompt-accept'),
|
||||||
|
)),
|
||||||
|
|
||||||
('aliases', sect.ValueList(
|
('aliases', sect.ValueList(
|
||||||
types.String(forbidden=' '), types.Command(),
|
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.
|
_elided_text: The current elided text.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, bar, elidemode=Qt.ElideRight):
|
def __init__(self, parent=None, elidemode=Qt.ElideRight):
|
||||||
super().__init__(bar)
|
super().__init__(parent)
|
||||||
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
|
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
|
||||||
self._elidemode = elidemode
|
self._elidemode = elidemode
|
||||||
self._elided_text = ''
|
self._elided_text = ''
|
||||||
@ -48,6 +48,7 @@ class TextBase(QLabel):
|
|||||||
Args:
|
Args:
|
||||||
width: The maximal width the text should take.
|
width: The maximal width the text should take.
|
||||||
"""
|
"""
|
||||||
|
if self.text is not None:
|
||||||
self._elided_text = self.fontMetrics().elidedText(
|
self._elided_text = self.fontMetrics().elidedText(
|
||||||
self.text(), self._elidemode, width, Qt.TextShowMnemonic)
|
self.text(), self._elidemode, width, Qt.TextShowMnemonic)
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ from qutebrowser.widgets.statusbar._text import Text
|
|||||||
from qutebrowser.widgets.statusbar._keystring import KeyString
|
from qutebrowser.widgets.statusbar._keystring import KeyString
|
||||||
from qutebrowser.widgets.statusbar._percentage import Percentage
|
from qutebrowser.widgets.statusbar._percentage import Percentage
|
||||||
from qutebrowser.widgets.statusbar._url import Url
|
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
|
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.setInterval(config.get('ui', 'message-timeout'))
|
||||||
self._text_pop_timer.timeout.connect(self._pop_text)
|
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.show_cmd.connect(self._show_cmd_widget)
|
||||||
self.cmd.hide_cmd.connect(self._hide_cmd_widget)
|
self.cmd.hide_cmd.connect(self._hide_cmd_widget)
|
||||||
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)
|
self._hbox.addLayout(self._stack)
|
||||||
|
|
||||||
@ -189,6 +195,24 @@ class StatusBar(QWidget):
|
|||||||
self._timer_was_active = False
|
self._timer_was_active = False
|
||||||
self._stack.setCurrentWidget(self.txt)
|
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):
|
def _disp_text(self, text, error, queue=False):
|
||||||
"""Inner logic for disp_error and disp_temp_text.
|
"""Inner logic for disp_error and disp_temp_text.
|
||||||
|
|
||||||
@ -292,6 +316,17 @@ class StatusBar(QWidget):
|
|||||||
self._text_pop_timer.setInterval(config.get('ui',
|
self._text_pop_timer.setInterval(config.get('ui',
|
||||||
'message-timeout'))
|
'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):
|
def resizeEvent(self, e):
|
||||||
"""Extend resizeEvent of QWidget to emit a resized signal afterwards.
|
"""Extend resizeEvent of QWidget to emit a resized signal afterwards.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user