Make the window title configurable.

Closes #489.
This commit is contained in:
Florian Bruhin 2015-01-28 08:40:16 +01:00
parent 68a0428a09
commit b9f16804f7
5 changed files with 106 additions and 13 deletions

View File

@ -35,6 +35,7 @@
|<<ui-css-media-type,css-media-type>>|Set the CSS media type.
|<<ui-remove-finished-downloads,remove-finished-downloads>>|Whether to remove finished downloads automatically.
|<<ui-hide-statusbar,hide-statusbar>>|Whether to hide the statusbar unless a message is shown.
|<<ui-window-title-format,window-title-format>>|The format to use for the window title. The following placeholders are defined:
|==============
.Quick reference for section ``network''
@ -466,6 +467,17 @@ Valid values:
Default: +pass:[false]+
[[ui-window-title-format]]
=== window-title-format
The format to use for the window title. The following placeholders are defined:
* `{perc}`: The percentage as a string like `[10%]`.
* `{perc_raw}`: The raw percentage, e.g. `10`
* `{title}`: The title of the current webpage
* `{title_sep}`: The string ` - ` if a title is set, empty otherwise.
Default: +pass:[{perc}{title}{title_sep}qutebrowser]+
== network
Settings related to the network.

View File

@ -241,6 +241,18 @@ DATA = collections.OrderedDict([
('hide-statusbar',
SettingValue(typ.Bool(), 'false'),
"Whether to hide the statusbar unless a message is shown."),
('window-title-format',
SettingValue(typ.FormatString(fields=['perc', 'perc_raw', 'title',
'title_sep']),
'{perc}{title}{title_sep}qutebrowser'),
"The format to use for the window title. The following placeholders "
"are defined:\n\n"
"* `{perc}`: The percentage as a string like `[10%]`.\n"
"* `{perc_raw}`: The raw percentage, e.g. `10`\n"
"* `{title}`: The title of the current webpage\n"
"* `{title_sep}`: The string ` - ` if a title is set, empty "
"otherwise.")
)),
('network', sect.KeyValue(

View File

@ -826,6 +826,32 @@ class Directory(BaseType):
return os.path.expanduser(value)
class FormatString(BaseType):
"""A string with '{foo}'-placeholders."""
typestr = 'format-string'
def __init__(self, fields, none_ok=False):
super().__init__(none_ok)
self.fields = fields
def validate(self, value):
if not value:
if self._none_ok:
return
else:
raise configexc.ValidationError(value, "may not be empty!")
s = self.transform(value)
try:
return s.format(**{k: '' for k in self.fields})
except KeyError as e:
raise configexc.ValidationError(value, "Invalid placeholder "
"{}".format(e))
except ValueError as e:
raise configexc.ValidationError(value, str(e))
class WebKitBytes(BaseType):
"""A size with an optional suffix.

View File

@ -113,6 +113,7 @@ class TabbedBrowser(tabwidget.TabWidget):
# https://github.com/The-Compiler/qutebrowser/issues/119
self.setIconSize(QSize(12, 12))
objreg.get('config').changed.connect(self.update_favicons)
objreg.get('config').changed.connect(self.update_window_title)
def __repr__(self):
return utils.get_repr(self, count=self.count())
@ -128,21 +129,23 @@ class TabbedBrowser(tabwidget.TabWidget):
w.append(self.widget(i))
return w
def _update_window_title(self):
@config.change_filter('ui', 'window-title-format')
def update_window_title(self):
"""Change the window title to match the current tab."""
idx = self.currentIndex()
tabtitle = self.tabText(idx)
widget = self.widget(idx)
fields = {}
if widget.load_status == webview.LoadStatus.loading:
title = '[{}%] '.format(widget.progress)
fields['perc'] = '[{}%] '.format(widget.progress)
else:
title = ''
if not tabtitle:
title += 'qutebrowser'
else:
title += '{} - qutebrowser'.format(tabtitle)
self.window().setWindowTitle(title)
fields['perc'] = ''
fields['perc_raw'] = widget.progress
fields['title'] = tabtitle
fields['title_sep'] = ' - ' if tabtitle else ''
fmt = config.get('ui', 'window-title-format')
self.window().setWindowTitle(fmt.format(**fields))
def _connect_tab_signals(self, tab):
"""Set up the needed signals for tab."""
@ -431,7 +434,7 @@ class TabbedBrowser(tabwidget.TabWidget):
else:
self.setTabIcon(idx, QIcon())
if idx == self.currentIndex():
self._update_window_title()
self.update_window_title()
@pyqtSlot()
def on_cur_load_started(self):
@ -467,7 +470,7 @@ class TabbedBrowser(tabwidget.TabWidget):
return
self.setTabText(idx, text.replace('&', '&&'))
if idx == self.currentIndex():
self._update_window_title()
self.update_window_title()
@pyqtSlot(webview.WebView, str)
def on_url_text_changed(self, tab, url):
@ -539,7 +542,7 @@ class TabbedBrowser(tabwidget.TabWidget):
scope='window', window=self._win_id)
self._now_focused = tab
self.current_tab_changed.emit(tab)
self._update_window_title()
self.update_window_title()
self._tab_insert_idx_left = self.currentIndex()
self._tab_insert_idx_right = self.currentIndex() + 1
@ -561,7 +564,7 @@ class TabbedBrowser(tabwidget.TabWidget):
color = utils.interpolate_color(start, stop, perc, system)
self.tabBar().set_tab_indicator_color(idx, color)
if idx == self.currentIndex():
self._update_window_title()
self.update_window_title()
def on_load_finished(self, tab):
"""Adjust tab indicator when loading finished.
@ -584,7 +587,7 @@ class TabbedBrowser(tabwidget.TabWidget):
color = utils.interpolate_color(start, stop, 100, system)
self.tabBar().set_tab_indicator_color(idx, color)
if idx == self.currentIndex():
self._update_window_title()
self.update_window_title()
def resizeEvent(self, e):
"""Extend resizeEvent of QWidget to emit a resized signal afterwards.

View File

@ -1976,5 +1976,45 @@ class UrlListTests(unittest.TestCase):
self.assertEqual(self.t.transform(''), None)
class FormatStringTests(unittest.TestCase):
"""Test FormatString."""
def setUp(self):
self.t = configtypes.FormatString(fields=('foo', 'bar'))
def test_transform(self):
"""Test if transform doesn't alter the value."""
self.assertEqual(self.t.transform('foo {bar} baz'), 'foo {bar} baz')
def test_validate_simple(self):
"""Test validate with a simple string."""
self.t.validate('foo bar baz')
def test_validate_placeholders(self):
"""Test validate with placeholders."""
self.t.validate('{foo} {bar} baz')
def test_validate_invalid_placeholders(self):
"""Test validate with invalid placeholders."""
with self.assertRaises(configexc.ValidationError):
self.t.validate('{foo} {bar} {baz}')
def test_validate_invalid_placeholders_syntax(self):
"""Test validate with invalid placeholders syntax."""
with self.assertRaises(configexc.ValidationError):
self.t.validate('{foo} {bar')
def test_validate_empty(self):
"""Test validate with empty string and none_ok = False."""
with self.assertRaises(configexc.ValidationError):
self.t.validate('')
def test_validate_empty_none_ok(self):
"""Test validate with empty string and none_ok = True."""
t = configtypes.FormatString(none_ok=True, fields=())
t.validate('')
if __name__ == '__main__':
unittest.main()