Make :undo re-open all tabs closed by :tab-only

This changes the undo stack from a list of UndoEntry objects to a list
of lists of UndoEntry objects, so groups of tabs can be added. Only
:tab-only does that, but it's exposed by TabbedBrowser.close_tab as a
keyword argument.
This commit is contained in:
Jan Verbeek 2017-11-06 19:32:10 +01:00
parent b1f1248a05
commit 3e8c84c018
4 changed files with 39 additions and 19 deletions

View File

@ -93,7 +93,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<tab-prev,tab-prev>>|Switch to the previous tab, or switch [count] tabs back. |<<tab-prev,tab-prev>>|Switch to the previous tab, or switch [count] tabs back.
|<<tab-take,tab-take>>|Take a tab from another window. |<<tab-take,tab-take>>|Take a tab from another window.
|<<unbind,unbind>>|Unbind a keychain. |<<unbind,unbind>>|Unbind a keychain.
|<<undo,undo>>|Re-open a closed tab. |<<undo,undo>>|Re-open the last closed tab or tabs.
|<<version,version>>|Show version information. |<<version,version>>|Show version information.
|<<view-source,view-source>>|Show the source of the current page in a new tab. |<<view-source,view-source>>|Show the source of the current page in a new tab.
|<<window-only,window-only>>|Close all windows except for the current one. |<<window-only,window-only>>|Close all windows except for the current one.
@ -1065,7 +1065,7 @@ Unbind a keychain.
[[undo]] [[undo]]
=== undo === undo
Re-open a closed tab. Re-open the last closed tab or tabs.
[[version]] [[version]]
=== version === version

View File

@ -968,13 +968,15 @@ class CommandDispatcher:
prev=prev, next_=next_, force=True)) prev=prev, next_=next_, force=True))
return return
first_tab = True
for i, tab in enumerate(self._tabbed_browser.widgets()): for i, tab in enumerate(self._tabbed_browser.widgets()):
if _to_close(i): if _to_close(i):
self._tabbed_browser.close_tab(tab) self._tabbed_browser.close_tab(tab, new_undo=first_tab)
first_tab = False
@cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.register(instance='command-dispatcher', scope='window')
def undo(self): def undo(self):
"""Re-open a closed tab.""" """Re-open the last closed tab or tabs."""
try: try:
self._tabbed_browser.undo() self._tabbed_browser.undo()
except IndexError: except IndexError:

View File

@ -71,7 +71,7 @@ class TabbedBrowser(tabwidget.TabWidget):
_tab_insert_idx_left: Where to insert a new tab with _tab_insert_idx_left: Where to insert a new tab with
tabs.new_tab_position set to 'prev'. tabs.new_tab_position set to 'prev'.
_tab_insert_idx_right: Same as above, for 'next'. _tab_insert_idx_right: Same as above, for 'next'.
_undo_stack: List of UndoEntry objects of closed tabs. _undo_stack: List of lists of UndoEntry objects of closed tabs.
shutting_down: Whether we're currently shutting down. shutting_down: Whether we're currently shutting down.
_local_marks: Jump markers local to each page _local_marks: Jump markers local to each page
_global_marks: Jump markers used across all pages _global_marks: Jump markers used across all pages
@ -270,12 +270,13 @@ class TabbedBrowser(tabwidget.TabWidget):
else: else:
yes_action() yes_action()
def close_tab(self, tab, *, add_undo=True): def close_tab(self, tab, *, add_undo=True, new_undo=True):
"""Close a tab. """Close a tab.
Args: Args:
tab: The QWebView to be closed. tab: The QWebView to be closed.
add_undo: Whether the tab close can be undone. add_undo: Whether the tab close can be undone.
new_undo: Whether the undo entry should be a new item in the stack.
""" """
last_close = config.val.tabs.last_close last_close = config.val.tabs.last_close
count = self.count() count = self.count()
@ -283,7 +284,7 @@ class TabbedBrowser(tabwidget.TabWidget):
if last_close == 'ignore' and count == 1: if last_close == 'ignore' and count == 1:
return return
self._remove_tab(tab, add_undo=add_undo) self._remove_tab(tab, add_undo=add_undo, new_undo=new_undo)
if count == 1: # We just closed the last tab above. if count == 1: # We just closed the last tab above.
if last_close == 'close': if last_close == 'close':
@ -296,12 +297,13 @@ class TabbedBrowser(tabwidget.TabWidget):
elif last_close == 'default-page': elif last_close == 'default-page':
self.openurl(config.val.url.default_page, newtab=True) self.openurl(config.val.url.default_page, newtab=True)
def _remove_tab(self, tab, *, add_undo=True, crashed=False): def _remove_tab(self, tab, *, add_undo=True, new_undo=True, crashed=False):
"""Remove a tab from the tab list and delete it properly. """Remove a tab from the tab list and delete it properly.
Args: Args:
tab: The QWebView to be closed. tab: The QWebView to be closed.
add_undo: Whether the tab close can be undone. add_undo: Whether the tab close can be undone.
new_undo: Whether the undo entry should be a new item in the stack.
crashed: Whether we're closing a tab with crashed renderer process. crashed: Whether we're closing a tab with crashed renderer process.
""" """
idx = self.indexOf(tab) idx = self.indexOf(tab)
@ -336,7 +338,10 @@ class TabbedBrowser(tabwidget.TabWidget):
else: else:
entry = UndoEntry(tab.url(), history_data, idx, entry = UndoEntry(tab.url(), history_data, idx,
tab.data.pinned) tab.data.pinned)
self._undo_stack.append(entry) if new_undo or not self._undo_stack:
self._undo_stack.append([entry])
else:
self._undo_stack[-1].append(entry)
tab.shutdown() tab.shutdown()
self.removeTab(idx) self.removeTab(idx)
@ -347,7 +352,7 @@ class TabbedBrowser(tabwidget.TabWidget):
tab.deleteLater() tab.deleteLater()
def undo(self): def undo(self):
"""Undo removing of a tab.""" """Undo removing of a tab or tabs."""
# Remove unused tab which may be created after the last tab is closed # Remove unused tab which may be created after the last tab is closed
last_close = config.val.tabs.last_close last_close = config.val.tabs.last_close
use_current_tab = False use_current_tab = False
@ -366,16 +371,17 @@ class TabbedBrowser(tabwidget.TabWidget):
use_current_tab = (only_one_tab_open and no_history and use_current_tab = (only_one_tab_open and no_history and
last_close_url_used) last_close_url_used)
entry = self._undo_stack.pop() for entry in reversed(self._undo_stack.pop()):
if use_current_tab:
self.openurl(entry.url, newtab=False)
newtab = self.widget(0)
use_current_tab = False
else:
newtab = self.tabopen(entry.url, background=False,
idx=entry.index)
if use_current_tab: newtab.history.deserialize(entry.history)
self.openurl(entry.url, newtab=False) self.set_tab_pinned(newtab, entry.pinned)
newtab = self.widget(0)
else:
newtab = self.tabopen(entry.url, background=False, idx=entry.index)
newtab.history.deserialize(entry.history)
self.set_tab_pinned(newtab, entry.pinned)
@pyqtSlot('QUrl', bool) @pyqtSlot('QUrl', bool)
def openurl(self, url, newtab): def openurl(self, url, newtab):

View File

@ -772,6 +772,18 @@ Feature: Tab management
- data/numbers/2.txt - data/numbers/2.txt
- data/numbers/3.txt - data/numbers/3.txt
Scenario: Undo the closing of tabs using :tab-only
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-focus 2
And I run :tab-only
And I run :undo
Then the following tabs should be open:
- data/numbers/1.txt (active)
- data/numbers/2.txt
- data/numbers/3.txt
# tabs.last_close # tabs.last_close
# FIXME:qtwebengine # FIXME:qtwebengine