Make it possible to configure tab titles.
This commit is contained in:
parent
1cf34e7984
commit
66d3ec1c08
@ -378,11 +378,12 @@ class CommandDispatcher:
|
||||
"""
|
||||
if bg and window:
|
||||
raise cmdexc.CommandError("Only one of -b/-w can be given!")
|
||||
curtab = self._current_widget()
|
||||
tabbed_browser = self._tabbed_browser(window)
|
||||
curtab = self._current_widget()
|
||||
cur_title = tabbed_browser.page_title(self._current_index())
|
||||
newtab = tabbed_browser.tabopen(background=bg, explicit=True)
|
||||
idx = tabbed_browser.indexOf(newtab)
|
||||
tabbed_browser.setTabText(idx, curtab.title().replace('&', '&&'))
|
||||
tabbed_browser.set_page_title(idx, cur_title)
|
||||
tabbed_browser.setTabIcon(idx, curtab.icon())
|
||||
newtab.keep_icon = True
|
||||
newtab.setZoomFactor(curtab.zoomFactor())
|
||||
@ -594,7 +595,7 @@ class CommandDispatcher:
|
||||
"""
|
||||
clipboard = QApplication.clipboard()
|
||||
if title:
|
||||
s = self._tabbed_browser().tabText(self._current_index())
|
||||
s = self._tabbed_browser().page_title(self._current_index())
|
||||
else:
|
||||
s = self._current_url().toString(
|
||||
QUrl.FullyEncoded | QUrl.RemovePassword)
|
||||
@ -788,7 +789,7 @@ class CommandDispatcher:
|
||||
tab = self._current_widget()
|
||||
cur_idx = self._current_index()
|
||||
icon = tabbed_browser.tabIcon(cur_idx)
|
||||
label = tabbed_browser.tabText(cur_idx)
|
||||
label = tabbed_browser.page_title(cur_idx)
|
||||
cmdutils.check_overflow(cur_idx, 'int')
|
||||
cmdutils.check_overflow(new_idx, 'int')
|
||||
tabbed_browser.setUpdatesEnabled(False)
|
||||
@ -850,7 +851,7 @@ class CommandDispatcher:
|
||||
idx = self._current_index()
|
||||
tabbed_browser = self._tabbed_browser()
|
||||
if idx != -1:
|
||||
env['QUTE_TITLE'] = tabbed_browser.tabText(idx)
|
||||
env['QUTE_TITLE'] = tabbed_browser.page_title(idx)
|
||||
|
||||
webview = tabbed_browser.currentWidget()
|
||||
if webview is not None and webview.hasSelection():
|
||||
|
@ -247,7 +247,7 @@ DATA = collections.OrderedDict([
|
||||
|
||||
('window-title-format',
|
||||
SettingValue(typ.FormatString(fields=['perc', 'perc_raw', 'title',
|
||||
'title_sep']),
|
||||
'title_sep', 'id']),
|
||||
'{perc}{title}{title_sep}qutebrowser'),
|
||||
"The format to use for the window title. The following placeholders "
|
||||
"are defined:\n\n"
|
||||
@ -255,7 +255,8 @@ DATA = collections.OrderedDict([
|
||||
"* `{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.")
|
||||
"otherwise.\n"
|
||||
"* `{id}`: The internal window ID of this window."),
|
||||
)),
|
||||
|
||||
('network', sect.KeyValue(
|
||||
@ -419,6 +420,20 @@ DATA = collections.OrderedDict([
|
||||
('tabs-are-windows',
|
||||
SettingValue(typ.Bool(), 'false'),
|
||||
"Whether to open windows instead of tabs."),
|
||||
|
||||
('title-format',
|
||||
SettingValue(typ.FormatString(fields=['perc', 'perc_raw', 'title',
|
||||
'title_sep', 'index', 'id']),
|
||||
'{index}: {title}'),
|
||||
"The format to use for the tab 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.\n"
|
||||
"* `{index}`: The index of this tab.\n"
|
||||
"* `{id}`: The internal tab ID of this tab."),
|
||||
)),
|
||||
|
||||
('storage', sect.KeyValue(
|
||||
|
@ -114,6 +114,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
self.setIconSize(QSize(12, 12))
|
||||
objreg.get('config').changed.connect(self.update_favicons)
|
||||
objreg.get('config').changed.connect(self.update_window_title)
|
||||
objreg.get('config').changed.connect(self.update_tab_titles)
|
||||
|
||||
def __repr__(self):
|
||||
return utils.get_repr(self, count=self.count())
|
||||
@ -133,7 +134,11 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
def update_window_title(self):
|
||||
"""Change the window title to match the current tab."""
|
||||
idx = self.currentIndex()
|
||||
tabtitle = self.tabText(idx)
|
||||
if idx == -1:
|
||||
# (e.g. last tab removed)
|
||||
log.webview.debug("Not updating window title because index is -1")
|
||||
return
|
||||
tabtitle = self.page_title(idx)
|
||||
widget = self.widget(idx)
|
||||
|
||||
fields = {}
|
||||
@ -144,6 +149,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
fields['perc_raw'] = widget.progress
|
||||
fields['title'] = tabtitle
|
||||
fields['title_sep'] = ' - ' if tabtitle else ''
|
||||
fields['id'] = self._win_id
|
||||
fmt = config.get('ui', 'window-title-format')
|
||||
self.window().setWindowTitle(fmt.format(**fields))
|
||||
|
||||
@ -429,6 +435,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
# We can get signals for tabs we already deleted...
|
||||
log.webview.debug("Got invalid tab {}!".format(tab))
|
||||
return
|
||||
self.update_tab_title(idx)
|
||||
if tab.keep_icon:
|
||||
tab.keep_icon = False
|
||||
else:
|
||||
@ -468,7 +475,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
# We can get signals for tabs we already deleted...
|
||||
log.webview.debug("Got invalid tab {}!".format(tab))
|
||||
return
|
||||
self.setTabText(idx, text.replace('&', '&&'))
|
||||
self.set_page_title(idx, text)
|
||||
if idx == self.currentIndex():
|
||||
self.update_window_title()
|
||||
|
||||
@ -489,8 +496,8 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
# We can get signals for tabs we already deleted...
|
||||
log.webview.debug("Got invalid tab {}!".format(tab))
|
||||
return
|
||||
if not self.tabText(idx):
|
||||
self.setTabText(idx, url)
|
||||
if not self.page_title(idx):
|
||||
self.set_page_title(idx, url)
|
||||
|
||||
@pyqtSlot(webview.WebView)
|
||||
def on_icon_changed(self, tab):
|
||||
@ -542,7 +549,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()
|
||||
QTimer.singleShot(0, self.update_window_title)
|
||||
self._tab_insert_idx_left = self.currentIndex()
|
||||
self._tab_insert_idx_right = self.currentIndex() + 1
|
||||
|
||||
@ -562,7 +569,8 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
stop = config.get('colors', 'tabs.indicator.stop')
|
||||
system = config.get('colors', 'tabs.indicator.system')
|
||||
color = utils.interpolate_color(start, stop, perc, system)
|
||||
self.tabBar().set_tab_indicator_color(idx, color)
|
||||
self.set_tab_indicator_color(idx, color)
|
||||
self.update_tab_title(idx)
|
||||
if idx == self.currentIndex():
|
||||
self.update_window_title()
|
||||
|
||||
@ -585,7 +593,8 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
stop = config.get('colors', 'tabs.indicator.stop')
|
||||
system = config.get('colors', 'tabs.indicator.system')
|
||||
color = utils.interpolate_color(start, stop, 100, system)
|
||||
self.tabBar().set_tab_indicator_color(idx, color)
|
||||
self.set_tab_indicator_color(idx, color)
|
||||
self.update_tab_title(idx)
|
||||
if idx == self.currentIndex():
|
||||
self.update_window_title()
|
||||
|
||||
|
@ -33,6 +33,7 @@ from PyQt5.QtGui import QIcon, QPalette, QColor
|
||||
|
||||
from qutebrowser.utils import qtutils, objreg, utils
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.browser import webview
|
||||
|
||||
|
||||
PM_TabBarPadding = QStyle.PM_CustomBase
|
||||
@ -47,6 +48,8 @@ class TabWidget(QTabWidget):
|
||||
bar = TabBar(win_id)
|
||||
self.setTabBar(bar)
|
||||
bar.tabCloseRequested.connect(self.tabCloseRequested)
|
||||
bar.tabMoved.connect(functools.partial(
|
||||
QTimer.singleShot, 0, self.update_tab_titles))
|
||||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.setDocumentMode(True)
|
||||
self.setElideMode(Qt.ElideRight)
|
||||
@ -68,6 +71,119 @@ class TabWidget(QTabWidget):
|
||||
tabbar.setSelectionBehaviorOnRemove(selection_behaviour)
|
||||
tabbar.refresh()
|
||||
|
||||
def set_tab_indicator_color(self, idx, color):
|
||||
"""Set the tab indicator color.
|
||||
|
||||
Args:
|
||||
idx: The tab index.
|
||||
color: A QColor.
|
||||
"""
|
||||
bar = self.tabBar()
|
||||
bar.set_tab_data(idx, 'indicator-color', color)
|
||||
bar.update(bar.tabRect(idx))
|
||||
|
||||
def set_page_title(self, idx, title):
|
||||
"""Set the tab title user data."""
|
||||
self.tabBar().set_tab_data(idx, 'page-title', title.replace('&', '&&'))
|
||||
self.update_tab_title(idx)
|
||||
|
||||
def page_title(self, idx):
|
||||
"""Get the tab title user data."""
|
||||
return self.tabBar().page_title(idx)
|
||||
|
||||
def update_tab_title(self, idx):
|
||||
"""Update the tab text for the given tab."""
|
||||
widget = self.widget(idx)
|
||||
page_title = self.page_title(idx)
|
||||
|
||||
fields = {}
|
||||
if widget.load_status == webview.LoadStatus.loading:
|
||||
fields['perc'] = '[{}%] '.format(widget.progress)
|
||||
else:
|
||||
fields['perc'] = ''
|
||||
fields['perc_raw'] = widget.progress
|
||||
fields['title'] = page_title
|
||||
fields['index'] = idx + 1
|
||||
fields['id'] = widget.tab_id
|
||||
fields['title_sep'] = ' - ' if page_title else ''
|
||||
|
||||
fmt = config.get('tabs', 'title-format')
|
||||
self.tabBar().setTabText(idx, fmt.format(**fields))
|
||||
|
||||
@config.change_filter('tabs', 'title-format')
|
||||
def update_tab_titles(self):
|
||||
"""Update all texts."""
|
||||
for idx in range(self.count()):
|
||||
self.update_tab_title(idx)
|
||||
|
||||
def tabInserted(self, idx):
|
||||
"""Update titles when a tab was inserted."""
|
||||
super().tabInserted(idx)
|
||||
self.update_tab_titles()
|
||||
|
||||
def tabRemoved(self, idx):
|
||||
"""Update titles when a tab was removed."""
|
||||
super().tabRemoved(idx)
|
||||
self.update_tab_titles()
|
||||
|
||||
def addTab(self, page, icon_or_text, text_or_empty=None):
|
||||
"""Override addTab to use our own text setting logic.
|
||||
|
||||
Unfortunately QTabWidget::addTab has these two overloads:
|
||||
- QWidget * page, const QIcon & icon, const QString & label
|
||||
- QWidget * page, const QString & label
|
||||
|
||||
This means we'll get different arguments based on the chosen overload.
|
||||
|
||||
Args:
|
||||
page: The QWidget to add.
|
||||
icon_or_text: Either the QIcon to add or the label.
|
||||
text_or_empty: Either the label or None.
|
||||
|
||||
Return:
|
||||
The index of the newly added tab.
|
||||
"""
|
||||
if text_or_empty is None:
|
||||
icon = None
|
||||
text = icon_or_text
|
||||
new_idx = super().addTab(page, '')
|
||||
else:
|
||||
icon = icon_or_text
|
||||
text = text_or_empty
|
||||
new_idx = super().addTab(page, icon, '')
|
||||
self.set_page_title(new_idx, text)
|
||||
return new_idx
|
||||
|
||||
def insertTab(self, idx, page, icon_or_text, text_or_empty=None):
|
||||
"""Override insertTab to use our own text setting logic.
|
||||
|
||||
Unfortunately QTabWidget::insertTab has these two overloads:
|
||||
- int index, QWidget * page, const QIcon & icon,
|
||||
const QString & label
|
||||
- int index, QWidget * page, const QString & label
|
||||
|
||||
This means we'll get different arguments based on the chosen overload.
|
||||
|
||||
Args:
|
||||
idx: Where to insert the widget.
|
||||
page: The QWidget to add.
|
||||
icon_or_text: Either the QIcon to add or the label.
|
||||
text_or_empty: Either the label or None.
|
||||
|
||||
Return:
|
||||
The index of the newly added tab.
|
||||
"""
|
||||
if text_or_empty is None:
|
||||
icon = None
|
||||
text = icon_or_text
|
||||
new_idx = super().insertTab(idx, page, '')
|
||||
else:
|
||||
icon = icon_or_text
|
||||
text = text_or_empty
|
||||
new_idx = super().insertTab(idx, page, icon, '')
|
||||
self.set_page_title(new_idx, text)
|
||||
return new_idx
|
||||
|
||||
|
||||
class TabBar(QTabBar):
|
||||
|
||||
@ -122,15 +238,38 @@ class TabBar(QTabBar):
|
||||
else:
|
||||
self.show()
|
||||
|
||||
def _set_tab_data(self, idx, key, value):
|
||||
def set_tab_data(self, idx, key, value):
|
||||
"""Set tab data as a dictionary."""
|
||||
if not 0 <= idx < self.count():
|
||||
raise IndexError("Tab index ({}) out of range ({})!".format(
|
||||
idx, self.count()))
|
||||
data = self.tabData(idx)
|
||||
if data is None:
|
||||
data = {}
|
||||
data[key] = value
|
||||
self.setTabData(idx, data)
|
||||
|
||||
def _tab_data(self, idx, key):
|
||||
def tab_data(self, idx, key):
|
||||
"""Get tab data for a given key."""
|
||||
return self.tabData(idx)[key]
|
||||
if not 0 <= idx < self.count():
|
||||
raise IndexError("Tab index ({}) out of range ({})!".format(
|
||||
idx, self.count()))
|
||||
data = self.tabData(idx)
|
||||
if data is None:
|
||||
data = {}
|
||||
return data[key]
|
||||
|
||||
def page_title(self, idx):
|
||||
"""Get the tab title user data.
|
||||
|
||||
Args:
|
||||
idx: The tab index to get the title for.
|
||||
handle_unset: Whether to return an emtpy string on KeyError.
|
||||
"""
|
||||
try:
|
||||
return self.tab_data(idx, 'page-title')
|
||||
except KeyError:
|
||||
return ''
|
||||
|
||||
def refresh(self):
|
||||
"""Properly repaint the tab bar and relayout tabs."""
|
||||
@ -138,16 +277,6 @@ class TabBar(QTabBar):
|
||||
# code sets layoutDirty so it actually relayouts the tabs.
|
||||
self.setIconSize(self.iconSize())
|
||||
|
||||
def set_tab_indicator_color(self, idx, color):
|
||||
"""Set the tab indicator color.
|
||||
|
||||
Args:
|
||||
idx: The tab index.
|
||||
color: A QColor.
|
||||
"""
|
||||
self._set_tab_data(idx, 'indicator-color', color)
|
||||
self.update(self.tabRect(idx))
|
||||
|
||||
@config.change_filter('fonts', 'tabbar')
|
||||
def set_font(self):
|
||||
"""Set the tabbar font."""
|
||||
@ -264,7 +393,7 @@ class TabBar(QTabBar):
|
||||
tab.palette.setColor(QPalette.Window, bg_color)
|
||||
tab.palette.setColor(QPalette.WindowText, fg_color)
|
||||
try:
|
||||
indicator_color = self._tab_data(idx, 'indicator-color')
|
||||
indicator_color = self.tab_data(idx, 'indicator-color')
|
||||
except KeyError:
|
||||
indicator_color = QColor()
|
||||
tab.palette.setColor(QPalette.Base, indicator_color)
|
||||
@ -275,15 +404,67 @@ class TabBar(QTabBar):
|
||||
p.drawControl(QStyle.CE_TabBarTab, tab)
|
||||
|
||||
def tabInserted(self, idx):
|
||||
"""Show the tabbar if configured to hide and >1 tab is open."""
|
||||
self._tabhide()
|
||||
self.setTabData(idx, {})
|
||||
"""Update visibility when a tab was inserted."""
|
||||
super().tabInserted(idx)
|
||||
self._tabhide()
|
||||
|
||||
def tabRemoved(self, idx):
|
||||
"""Hide the tabbar if configured when only one tab is open."""
|
||||
self._tabhide()
|
||||
"""Update visibility when a tab was removed."""
|
||||
super().tabRemoved(idx)
|
||||
self._tabhide()
|
||||
|
||||
def addTab(self, icon_or_text, text_or_empty=None):
|
||||
"""Override addTab to use our own text setting logic.
|
||||
|
||||
Unfortunately QTabBar::addTab has these two overloads:
|
||||
- const QIcon & icon, const QString & label
|
||||
- const QString & label
|
||||
|
||||
This means we'll get different arguments based on the chosen overload.
|
||||
|
||||
Args:
|
||||
icon_or_text: Either the QIcon to add or the label.
|
||||
text_or_empty: Either the label or None.
|
||||
|
||||
Return:
|
||||
The index of the newly added tab.
|
||||
"""
|
||||
if text_or_empty is None:
|
||||
icon = None
|
||||
text = icon_or_text
|
||||
new_idx = super().addTab('')
|
||||
else:
|
||||
icon = icon_or_text
|
||||
text = text_or_empty
|
||||
new_idx = super().addTab(icon, '')
|
||||
self.set_page_title(new_idx, text)
|
||||
|
||||
def insertTab(self, idx, icon_or_text, text_or_empty=None):
|
||||
"""Override insertTab to use our own text setting logic.
|
||||
|
||||
Unfortunately QTabBar::insertTab has these two overloads:
|
||||
- int index, const QIcon & icon, const QString & label
|
||||
- int index, const QString & label
|
||||
|
||||
This means we'll get different arguments based on the chosen overload.
|
||||
|
||||
Args:
|
||||
idx: Where to insert the widget.
|
||||
icon_or_text: Either the QIcon to add or the label.
|
||||
text_or_empty: Either the label or None.
|
||||
|
||||
Return:
|
||||
The index of the newly added tab.
|
||||
"""
|
||||
if text_or_empty is None:
|
||||
icon = None
|
||||
text = icon_or_text
|
||||
new_idx = super().InsertTab(idx, '')
|
||||
else:
|
||||
icon = icon_or_text
|
||||
text = text_or_empty
|
||||
new_idx = super().insertTab(idx, icon, '')
|
||||
self.set_page_title(new_idx, text)
|
||||
|
||||
|
||||
class TabBarStyle(QCommonStyle):
|
||||
|
Loading…
Reference in New Issue
Block a user