Merge commit '419793c0b9ff4f293babea7623dcaf4787bbaa35'

This commit is contained in:
Florian Bruhin 2017-05-22 07:55:44 +02:00
commit c32c01ffc0
13 changed files with 379 additions and 37 deletions

View File

@ -153,6 +153,7 @@ Contributors, sorted by the number of commits in descending order:
* Lamar Pavel
* Marshall Lochbaum
* Bruno Oliveira
* thuck
* Martin Tournoij
* Imran Sobir
* Alexander Cogneau
@ -163,6 +164,7 @@ Contributors, sorted by the number of commits in descending order:
* Joel Torstensson
* Patric Schmitz
* Tarcisio Fedrizzi
* Jay Kamat
* Claude
* Fritz Reichwald
* Corentin Julé
@ -184,7 +186,6 @@ Contributors, sorted by the number of commits in descending order:
* ZDarian
* Milan Svoboda
* John ShaggyTwoDope Jenkins
* Jay Kamat
* Clayton Craft
* Peter Vilim
* Jacob Sword
@ -218,7 +219,6 @@ Contributors, sorted by the number of commits in descending order:
* Jussi Timperi
* Cosmin Popescu
* Brian Jackson
* thuck
* sbinix
* rsteube
* neeasade

View File

@ -82,6 +82,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<tab-move,tab-move>>|Move the current tab according to the argument and [count].
|<<tab-next,tab-next>>|Switch to the next tab, or switch [count] tabs forward.
|<<tab-only,tab-only>>|Close all tabs except for the current one.
|<<tab-pin,tab-pin>>|Pin/Unpin the current/[count]th tab.
|<<tab-prev,tab-prev>>|Switch to the previous tab, or switch [count] tabs back.
|<<unbind,unbind>>|Unbind a keychain.
|<<undo,undo>>|Re-open a closed tab (optionally skipping [count] closed tabs).
@ -835,7 +836,7 @@ Duplicate the current tab.
[[tab-close]]
=== tab-close
Syntax: +:tab-close [*--prev*] [*--next*] [*--opposite*]+
Syntax: +:tab-close [*--prev*] [*--next*] [*--opposite*] [*--force*]+
Close the current/[count]th tab.
@ -844,6 +845,7 @@ Close the current/[count]th tab.
* +*-n*+, +*--next*+: Force selecting the tab after the current tab.
* +*-o*+, +*--opposite*+: Force selecting the tab in the opposite direction of what's configured in 'tabs->select-on-remove'.
* +*-f*+, +*--force*+: Avoid confirmation for pinned tabs.
==== count
The tab index to close
@ -896,13 +898,23 @@ How many tabs to switch forward.
[[tab-only]]
=== tab-only
Syntax: +:tab-only [*--prev*] [*--next*]+
Syntax: +:tab-only [*--prev*] [*--next*] [*--force*]+
Close all tabs except for the current one.
==== optional arguments
* +*-p*+, +*--prev*+: Keep tabs before the current.
* +*-n*+, +*--next*+: Keep tabs after the current.
* +*-f*+, +*--force*+: Avoid confirmation for pinned tabs.
[[tab-pin]]
=== tab-pin
Pin/Unpin the current/[count]th tab.
Pinning a tab shrinks it to tabs->pinned-width size. Attempting to close a pinned tab will cause a confirmation, unless --force is passed.
==== count
The tab index to pin or unpin
[[tab-prev]]
=== tab-prev

View File

@ -128,9 +128,11 @@
|<<tabs-show-favicons,show-favicons>>|Whether to show favicons in the tab bar.
|<<tabs-favicon-scale,favicon-scale>>|Scale for favicons in the tab bar. The tab size is unchanged, so big favicons also require extra `tabs->padding`.
|<<tabs-width,width>>|The width of the tab bar if it's vertical, in px or as percentage of the window.
|<<tabs-pinned-width,pinned-width>>|The width for pinned tabs with a horizontal tabbar, in px.
|<<tabs-indicator-width,indicator-width>>|Width of the progress indicator (0 to disable).
|<<tabs-tabs-are-windows,tabs-are-windows>>|Whether to open windows instead of tabs.
|<<tabs-title-format,title-format>>|The format to use for the tab title. The following placeholders are defined:
|<<tabs-title-format-pinned,title-format-pinned>>|The format to use for the tab title for pinned tabs. The same placeholders like for title-format are defined.
|<<tabs-title-alignment,title-alignment>>|Alignment of the text inside of tabs
|<<tabs-mousewheel-tab-switching,mousewheel-tab-switching>>|Switch between tabs using the mouse wheel.
|<<tabs-padding,padding>>|Padding for tabs (top, bottom, left, right).
@ -1221,6 +1223,12 @@ The width of the tab bar if it's vertical, in px or as percentage of the window.
Default: +pass:[20%]+
[[tabs-pinned-width]]
=== pinned-width
The width for pinned tabs with a horizontal tabbar, in px.
Default: +pass:[43]+
[[tabs-indicator-width]]
=== indicator-width
Width of the progress indicator (0 to disable).
@ -1254,6 +1262,12 @@ The format to use for the tab title. The following placeholders are defined:
Default: +pass:[{index}: {title}]+
[[tabs-title-format-pinned]]
=== title-format-pinned
The format to use for the tab title for pinned tabs. The same placeholders like for title-format are defined.
Default: +pass:[{index}]+
[[tabs-title-alignment]]
=== title-alignment
Alignment of the text inside of tabs

View File

@ -96,6 +96,7 @@ class TabData:
viewing_source: Set if we're currently showing a source view.
override_target: Override for open_target for fake clicks (like hints).
Only used for QtWebKit.
pinned: Flag to pin the tab.
"""
def __init__(self):
@ -103,6 +104,7 @@ class TabData:
self.viewing_source = False
self.inspector = None
self.override_target = None
self.pinned = False
class AbstractAction:

View File

@ -202,24 +202,21 @@ class CommandDispatcher:
"{!r}!".format(conf_selection))
return None
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('count', count=True)
def tab_close(self, prev=False, next_=False, opposite=False, count=None):
"""Close the current/[count]th tab.
def _tab_close(self, tab, prev=False, next_=False, opposite=False):
"""Helper function for tab_close be able to handle message.async.
Args:
tab: Tab object to select be closed.
prev: Force selecting the tab before the current tab.
next_: Force selecting the tab after the current tab.
opposite: Force selecting the tab in the opposite direction of
what's configured in 'tabs->select-on-remove'.
count: The tab index to close, or None
"""
tab = self._cntwidget(count)
if tab is None:
return
tabbar = self._tabbed_browser.tabBar()
selection_override = self._get_selection_override(prev, next_,
opposite)
if selection_override is None:
self._tabbed_browser.close_tab(tab)
else:
@ -228,6 +225,63 @@ class CommandDispatcher:
self._tabbed_browser.close_tab(tab)
tabbar.setSelectionBehaviorOnRemove(old_selection_behavior)
def _tab_close_prompt_if_pinned(self, tab, force, yes_action):
"""Helper method for tab_close.
If tab is pinned, prompt. If everything is good, run yes_action.
"""
if tab.data.pinned and not force:
message.confirm_async(
title='Pinned Tab',
text="Are you sure you want to close a pinned tab?",
yes_action=yes_action, default=False)
else:
yes_action()
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('count', count=True)
def tab_close(self, prev=False, next_=False, opposite=False,
force=False, count=None):
"""Close the current/[count]th tab.
Args:
prev: Force selecting the tab before the current tab.
next_: Force selecting the tab after the current tab.
opposite: Force selecting the tab in the opposite direction of
what's configured in 'tabs->select-on-remove'.
force: Avoid confirmation for pinned tabs.
count: The tab index to close, or None
"""
tab = self._cntwidget(count)
if tab is None:
return
close = functools.partial(self._tab_close, tab, prev,
next_, opposite)
self._tab_close_prompt_if_pinned(tab, force, close)
@cmdutils.register(instance='command-dispatcher', scope='window',
name='tab-pin')
@cmdutils.argument('count', count=True)
def tab_pin(self, count=None):
"""Pin/Unpin the current/[count]th tab.
Pinning a tab shrinks it to tabs->pinned-width size.
Attempting to close a pinned tab will cause a confirmation,
unless --force is passed.
Args:
count: The tab index to pin or unpin, or None
"""
tab = self._cntwidget(count)
if tab is None:
return
to_pin = not tab.data.pinned
tab_index = self._current_index() if count is None else count - 1
cmdutils.check_overflow(tab_index + 1, 'int')
self._tabbed_browser.set_tab_pinned(tab_index, to_pin)
@cmdutils.register(instance='command-dispatcher', name='open',
maxsplit=0, scope='window')
@cmdutils.argument('url', completion=usertypes.Completion.url)
@ -275,6 +329,8 @@ class CommandDispatcher:
else:
# Explicit count with a tab that doesn't exist.
return
elif curtab.data.pinned:
message.info("Tab is pinned!")
else:
curtab.openurl(cur_url)
@ -457,6 +513,7 @@ class CommandDispatcher:
newtab.data.keep_icon = True
newtab.history.deserialize(history)
newtab.zoom.set_factor(curtab.zoom.factor())
new_tabbed_browser.set_tab_pinned(idx, curtab.data.pinned)
return newtab
@cmdutils.register(instance='command-dispatcher', scope='window')
@ -832,22 +889,36 @@ class CommandDispatcher:
message.info("Zoom level: {}%".format(level), replace=True)
@cmdutils.register(instance='command-dispatcher', scope='window')
def tab_only(self, prev=False, next_=False):
def tab_only(self, prev=False, next_=False, force=False):
"""Close all tabs except for the current one.
Args:
prev: Keep tabs before the current.
next_: Keep tabs after the current.
force: Avoid confirmation for pinned tabs.
"""
cmdutils.check_exclusive((prev, next_), 'pn')
cur_idx = self._tabbed_browser.currentIndex()
assert cur_idx != -1
def _to_close(i):
"""Helper method to check if a tab should be closed or not."""
return not (i == cur_idx or
(prev and i < cur_idx) or
(next_ and i > cur_idx))
# Check to see if we are closing any pinned tabs
if not force:
for i, tab in enumerate(self._tabbed_browser.widgets()):
if _to_close(i) and tab.data.pinned:
self._tab_close_prompt_if_pinned(
tab, force,
lambda: self.tab_only(
prev=prev, next_=next_, force=True))
return
for i, tab in enumerate(self._tabbed_browser.widgets()):
if (i == cur_idx or (prev and i < cur_idx) or
(next_ and i > cur_idx)):
continue
else:
if _to_close(i):
self._tabbed_browser.close_tab(tab)
@cmdutils.register(instance='command-dispatcher', scope='window')

View File

@ -690,6 +690,11 @@ def data(readonly=False):
"The width of the tab bar if it's vertical, in px or as "
"percentage of the window."),
('pinned-width',
SettingValue(typ.Int(minval=10),
'43'),
"The width for pinned tabs with a horizontal tabbar, in px."),
('indicator-width',
SettingValue(typ.Int(minval=0), '3'),
"Width of the progress indicator (0 to disable)."),
@ -716,6 +721,14 @@ def data(readonly=False):
"* `{host}`: The host of the current web page.\n"
"* `{backend}`: Either 'webkit' or 'webengine'"),
('title-format-pinned',
SettingValue(typ.FormatString(
fields=['perc', 'perc_raw', 'title', 'title_sep', 'index',
'id', 'scroll_pos', 'host'], none_ok=True),
'{index}'),
"The format to use for the tab title for pinned tabs. "
"The same placeholders like for title-format are defined."),
('title-alignment',
SettingValue(typ.TextAlignment(), 'left'),
"Alignment of the text inside of tabs"),
@ -1716,6 +1729,7 @@ KEY_DATA = collections.OrderedDict([
('follow-selected', RETURN_KEYS),
('follow-selected -t', ['<Ctrl-Return>', '<Ctrl-Enter>']),
('repeat-command', ['.']),
('tab-pin', ['<Ctrl-p>']),
('record-macro', ['q']),
('run-macro', ['@']),
])),

View File

@ -34,7 +34,8 @@ from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg,
urlutils, message, jinja)
UndoEntry = collections.namedtuple('UndoEntry', ['url', 'history', 'index'])
UndoEntry = collections.namedtuple('UndoEntry',
['url', 'history', 'index', 'pinned'])
class TabDeletedError(Exception):
@ -244,6 +245,10 @@ class TabbedBrowser(tabwidget.TabWidget):
if last_close == 'ignore' and count == 1:
return
# If we are removing a pinned tab, decrease count
if tab.data.pinned:
self.tabBar().pinned_count -= 1
self._remove_tab(tab, add_undo=add_undo)
if count == 1: # We just closed the last tab above.
@ -294,7 +299,8 @@ class TabbedBrowser(tabwidget.TabWidget):
except browsertab.WebTabError:
pass # special URL
else:
entry = UndoEntry(tab.url(), history_data, idx)
entry = UndoEntry(tab.url(), history_data, idx,
tab.data.pinned)
self._undo_stack.append(entry)
tab.shutdown()
@ -325,7 +331,7 @@ class TabbedBrowser(tabwidget.TabWidget):
use_current_tab = (only_one_tab_open and no_history and
last_close_url_used)
url, history_data, idx = self._undo_stack.pop()
url, history_data, idx, pinned = self._undo_stack.pop()
if use_current_tab:
self.openurl(url, newtab=False)
@ -334,6 +340,7 @@ class TabbedBrowser(tabwidget.TabWidget):
newtab = self.tabopen(url, background=False, idx=idx)
newtab.history.deserialize(history_data)
self.set_tab_pinned(idx, pinned)
@pyqtSlot('QUrl', bool)
def openurl(self, url, newtab):

View File

@ -94,6 +94,32 @@ class TabWidget(QTabWidget):
bar.set_tab_data(idx, 'indicator-color', color)
bar.update(bar.tabRect(idx))
def set_tab_pinned(self, idx, pinned, *, loading=False):
"""Set the tab status as pinned.
Args:
idx: The tab index.
pinned: Pinned tab state to set.
loading: Whether to ignore current data state when
counting pinned_count.
"""
bar = self.tabBar()
tab = self.widget(idx)
# Only modify pinned_count if we had a change
# always modify pinned_count if we are loading
if tab.data.pinned != pinned or loading:
if pinned:
bar.pinned_count += 1
elif not pinned:
bar.pinned_count -= 1
bar.set_tab_data(idx, 'pinned', pinned)
tab.data.pinned = pinned
self.update_tab_title(idx)
bar.refresh()
def tab_indicator_color(self, idx):
"""Get the tab indicator color for the given index."""
return self.tabBar().tab_indicator_color(idx)
@ -109,12 +135,19 @@ class TabWidget(QTabWidget):
def update_tab_title(self, idx):
"""Update the tab text for the given tab."""
tab = self.widget(idx)
fields = self.get_tab_fields(idx)
fields['title'] = fields['title'].replace('&', '&&')
fields['index'] = idx + 1
fmt = config.get('tabs', 'title-format')
title = '' if fmt is None else fmt.format(**fields)
fmt_pinned = config.get('tabs', 'title-format-pinned')
if tab.data.pinned:
title = '' if fmt_pinned is None else fmt_pinned.format(**fields)
else:
title = '' if fmt is None else fmt.format(**fields)
self.tabBar().setTabText(idx, title)
def get_tab_fields(self, idx):
@ -155,11 +188,12 @@ class TabWidget(QTabWidget):
fields['scroll_pos'] = scroll_pos
return fields
@config.change_filter('tabs', 'title-format')
def update_tab_titles(self):
def update_tab_titles(self, section='tabs', option='title-format'):
"""Update all texts."""
for idx in range(self.count()):
self.update_tab_title(idx)
if section == 'tabs' and option in ['title-format',
'title-format-pinned']:
for idx in range(self.count()):
self.update_tab_title(idx)
def tabInserted(self, idx):
"""Update titles when a tab was inserted."""
@ -285,6 +319,7 @@ class TabBar(QTabBar):
self._auto_hide_timer.timeout.connect(self._tabhide)
self.setAutoFillBackground(True)
self.set_colors()
self.pinned_count = 0
config_obj.changed.connect(self.set_colors)
QTimer.singleShot(0, self._tabhide)
config_obj.changed.connect(self.on_tab_colors_changed)
@ -472,9 +507,31 @@ class TabBar(QTabBar):
# get scroll buttons as soon as needed.
size = minimum_size
else:
tab_width_pinned_conf = config.get('tabs', 'pinned-width')
try:
pinned = self.tab_data(index, 'pinned')
except KeyError:
pinned = False
if pinned:
size = QSize(tab_width_pinned_conf, height)
qtutils.ensure_valid(size)
return size
# If we *do* have enough space, tabs should occupy the whole window
# width.
width = self.width() / self.count()
# width. If there are pinned tabs their size will be subtracted
# from the total window width.
# During shutdown the self.count goes down,
# but the self.pinned_count not - this generates some odd behavior.
# To avoid this we compare self.count against self.pinned_count.
if self.pinned_count > 0 and self.count() > self.pinned_count:
pinned_width = tab_width_pinned_conf * self.pinned_count
no_pinned_width = self.width() - pinned_width
width = no_pinned_width / (self.count() - self.pinned_count)
else:
width = self.width() / self.count()
# If width is not divisible by count, add a pixel to some tabs so
# that there is no ugly leftover space.
if index < self.width() % self.count():

View File

@ -195,6 +195,9 @@ class SessionManager(QObject):
if 'scroll-pos' in user_data:
pos = user_data['scroll-pos']
data['scroll-pos'] = {'x': pos.x(), 'y': pos.y()}
data['pinned'] = tab.data.pinned
return data
def _save_tab(self, tab, active):
@ -352,6 +355,9 @@ class SessionManager(QObject):
pos = histentry['scroll-pos']
user_data['scroll-pos'] = QPoint(pos['x'], pos['y'])
if 'pinned' in histentry:
new_tab.data.pinned = histentry['pinned']
active = histentry.get('active', False)
url = QUrl.fromEncoded(histentry['url'].encode('ascii'))
if 'original-url' in histentry:
@ -397,6 +403,9 @@ class SessionManager(QObject):
self._load_tab(new_tab, tab)
if tab.get('active', False):
tab_to_focus = i
if new_tab.data.pinned:
tabbed_browser.set_tab_pinned(
i, new_tab.data.pinned, loading=True)
if tab_to_focus is not None:
tabbed_browser.setCurrentIndex(tab_to_focus)
if win.get('active', False):

View File

@ -159,8 +159,8 @@ def clean_open_tabs(quteproc):
"""Clean up open windows and tabs."""
quteproc.set_setting('tabs', 'last-close', 'blank')
quteproc.send_cmd(':window-only')
quteproc.send_cmd(':tab-only')
quteproc.send_cmd(':tab-close')
quteproc.send_cmd(':tab-only --force')
quteproc.send_cmd(':tab-close --force')
quteproc.wait_for_load_finished_url('about:blank')
@ -543,31 +543,46 @@ def check_open_tabs(quteproc, request, tabs):
"""
session = quteproc.get_session()
active_suffix = ' (active)'
pinned_suffix = ' (pinned)'
tabs = tabs.splitlines()
assert len(session['windows']) == 1
assert len(session['windows'][0]['tabs']) == len(tabs)
# If we don't have (active) anywhere, don't check it
has_active = any(line.endswith(active_suffix) for line in tabs)
has_active = any(active_suffix in line for line in tabs)
has_pinned = any(pinned_suffix in line for line in tabs)
for i, line in enumerate(tabs):
line = line.strip()
assert line.startswith('- ')
line = line[2:] # remove "- " prefix
if line.endswith(active_suffix):
path = line[:-len(active_suffix)]
active = True
else:
path = line
active = False
active = False
pinned = False
while line.endswith(active_suffix) or line.endswith(pinned_suffix):
if line.endswith(active_suffix):
# active
line = line[:-len(active_suffix)]
active = True
else:
# pinned
line = line[:-len(pinned_suffix)]
pinned = True
session_tab = session['windows'][0]['tabs'][i]
assert session_tab['history'][-1]['url'] == quteproc.path_to_url(path)
current_page = session_tab['history'][-1]
assert current_page['url'] == quteproc.path_to_url(line)
if active:
assert session_tab['active']
elif has_active:
assert 'active' not in session_tab
if pinned:
assert current_page['pinned']
elif has_pinned:
assert not current_page['pinned']
@bdd.then(bdd.parsers.re(r'the (?P<what>primary selection|clipboard) should '
r'contain "(?P<content>.*)"'))

View File

@ -342,7 +342,7 @@ Feature: Saving and loading sessions
Scenario: Loading a directory
When I run :session-load (tmpdir)
Then the error "Error while loading session: *" should be shown
Scenario: Loading internal session without --force
When I run :session-save --force _internal
And I run :session-load _internal
@ -367,3 +367,24 @@ Feature: Saving and loading sessions
Scenario: Loading a session which doesn't exist
When I run :session-load inexistent_session
Then the error "Session inexistent_session not found!" should be shown
# Test load/save of pinned tabs
Scenario: Saving/Loading a session with pinned tabs
When I open data/numbers/1.txt
And I open data/numbers/2.txt in a new tab
And I open data/numbers/3.txt in a new tab
And I run :tab-pin with count 2
And I run :session-save pin_session
And I run :tab-only --force
And I run :tab-close --force
And I run :session-load -c pin_session
And I wait until data/numbers/3.txt is loaded
And I run :tab-focus 2
And I run :open hello world
Then the message "Tab is pinned!" should be shown
And the following tabs should be open:
- data/numbers/1.txt
- data/numbers/2.txt (active) (pinned)
- data/numbers/3.txt

View File

@ -1026,3 +1026,121 @@ Feature: Tab management
- tabs:
- history:
- url: http://localhost:*/data/hello.txt
# :tab-pin
Scenario: :tab-pin command
When I open data/numbers/1.txt
And I open data/numbers/2.txt in a new tab
And I open data/numbers/3.txt in a new tab
And I run :tab-pin
Then the following tabs should be open:
- data/numbers/1.txt
- data/numbers/2.txt
- data/numbers/3.txt (active) (pinned)
Scenario: :tab-pin unpin
When I open data/numbers/1.txt
And I run :tab-pin
And I open data/numbers/2.txt in a new tab
And I open data/numbers/3.txt in a new tab
And I run :tab-pin
And I run :tab-pin
Then the following tabs should be open:
- data/numbers/1.txt (pinned)
- data/numbers/2.txt
- data/numbers/3.txt (active)
Scenario: :tab-pin to index 2
When I open data/numbers/1.txt
And I open data/numbers/2.txt in a new tab
And I open data/numbers/3.txt in a new tab
And I run :tab-pin with count 2
Then the following tabs should be open:
- data/numbers/1.txt
- data/numbers/2.txt (pinned)
- data/numbers/3.txt (active)
Scenario: Pinned :tab-close prompt yes
When I open data/numbers/1.txt
And I run :tab-pin
And I open data/numbers/2.txt in a new tab
And I run :tab-pin
And I run :tab-close
And I wait for "*want to close a pinned tab*" in the log
And I run :prompt-accept yes
Then the following tabs should be open:
- data/numbers/1.txt (active) (pinned)
Scenario: Pinned :tab-close prompt no
When I open data/numbers/1.txt
And I run :tab-pin
And I open data/numbers/2.txt in a new tab
And I run :tab-pin
And I run :tab-close
And I wait for "*want to close a pinned tab*" in the log
And I run :prompt-accept no
Then the following tabs should be open:
- data/numbers/1.txt (pinned)
- data/numbers/2.txt (active) (pinned)
Scenario: Pinned :tab-only prompt yes
When I open data/numbers/1.txt
And I run :tab-pin
And I open data/numbers/2.txt in a new tab
And I run :tab-pin
And I run :tab-next
And I run :tab-only
And I wait for "*want to close a pinned tab*" in the log
And I run :prompt-accept yes
Then the following tabs should be open:
- data/numbers/1.txt (active) (pinned)
Scenario: Pinned :tab-only prompt no
When I open data/numbers/1.txt
And I run :tab-pin
And I open data/numbers/2.txt in a new tab
And I run :tab-pin
And I run :tab-next
And I run :tab-only
And I wait for "*want to close a pinned tab*" in the log
And I run :prompt-accept no
Then the following tabs should be open:
- data/numbers/1.txt (active) (pinned)
- data/numbers/2.txt (pinned)
Scenario: Pinned :tab-only close all but pinned tab
When I open data/numbers/1.txt
And I open data/numbers/2.txt in a new tab
And I run :tab-pin
And I run :tab-only
Then the following tabs should be open:
- data/numbers/2.txt (active) (pinned)
Scenario: :tab-pin open url
When I open data/numbers/1.txt
And I run :tab-pin
And I open data/numbers/2.txt without waiting
Then the message "Tab is pinned!" should be shown
And the following tabs should be open:
- data/numbers/1.txt (active) (pinned)
Scenario: Cloning a pinned tab
When I open data/numbers/1.txt
And I run :tab-pin
And I run :tab-clone
And I wait until data/numbers/1.txt is loaded
Then the following tabs should be open:
- data/numbers/1.txt (pinned)
- data/numbers/1.txt (pinned) (active)
Scenario: Undo a pinned tab
When I open data/numbers/1.txt
And I open data/numbers/2.txt in a new tab
And I run :tab-pin
And I run :tab-close --force
And I run :undo
And I wait until data/numbers/2.txt is loaded
Then the following tabs should be open:
- data/numbers/1.txt
- data/numbers/2.txt (pinned) (active)

View File

@ -49,6 +49,8 @@ class TestTabWidget:
'indicator-width': 3,
'indicator-padding': configtypes.PaddingValues(2, 2, 0, 4),
'title-format': '{index}: {title}',
'title-format-pinned': '{index}',
'pinned-width': 43,
'title-alignment': Qt.AlignLeft,
},
'colors': {