From 6f610e9c44e381e11aeee678b32455bef9119d59 Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 6 Nov 2016 15:52:23 +0100 Subject: [PATCH 01/56] Initial development to support pin tabs #926 Done so far: Two new commands pin/unpin, both accept a index to help the organization (maybe this should be more a flag and not exactly two commands) Crtl+p to pin, Crtl+O to unpin (not sure which should a good default shortcut) If user tries to close a pinned tab it's asked to confirm If user tries to open a URL in a pinned tab it receives a message with a information that the tab is pinned and ignore the openurl command Preserve the pinned information across restart if session is activated Missing: Visual indication of the tab being pinned Tab appearance being distinct over other tabs Make pinned tabs to be the firsts on the tab bar This is not ready, but it would be good to get some feedback earlier --- qutebrowser/browser/browsertab.py | 1 + qutebrowser/browser/commands.py | 34 +++++++++++++++++++++++++++++++ qutebrowser/config/configdata.py | 2 ++ qutebrowser/misc/sessions.py | 6 ++++++ 4 files changed, 43 insertions(+) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 22f1c7e51..67d2bee44 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -560,6 +560,7 @@ class AbstractTab(QWidget): self._mouse_event_filter = mouse.MouseEventFilter( self, widget_class=self.WIDGET_CLASS, parent=self) self.backend = None + self.pin = False # FIXME:qtwebengine Should this be public api via self.hints? # Also, should we get it out of objreg? diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 2c0e01b5f..3ee28493e 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -227,6 +227,14 @@ class CommandDispatcher: tab = self._cntwidget(count) if tab is None: return + + if tab.pin is True: + result = message.ask("Are you sure you want to close a pinned tab?", + mode=usertypes.PromptMode.yesno, default=False) + + if result is False or result is None: + return + tabbar = self._tabbed_browser.tabBar() selection_override = self._get_selection_override(left, right, opposite) @@ -238,6 +246,29 @@ class CommandDispatcher: self._tabbed_browser.close_tab(tab) tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) + @cmdutils.register(instance='command-dispatcher', scope='window', name='pin') + @cmdutils.argument('index') + @cmdutils.argument('count', count=True) + def tab_pin(self, index=1, count=None): + tab = self._cntwidget(count) + if tab is None: + return + tab.pin = True + self.tab_move(int(index)) + + @cmdutils.register(instance='command-dispatcher', scope='window', name='unpin') + @cmdutils.argument('index') + @cmdutils.argument('count', count=True) + def tab_unpin(self, index=None, count=None): + tab = self._cntwidget(count) + if tab is None: + return + tab.pin = False + if index is not None: + self.tab_move(int(index)) + else: + self.tab_move(self._count()) + @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') @cmdutils.argument('url', completion=usertypes.Completion.url) @@ -281,6 +312,9 @@ class CommandDispatcher: else: # Explicit count with a tab that doesn't exist. return + elif curtab.pin is True: + message.info("Tab is pinned!") + else: curtab.openurl(cur_url) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index bad6fa51a..3219b5c9b 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1652,6 +1652,8 @@ KEY_DATA = collections.OrderedDict([ ('follow-selected', RETURN_KEYS), ('follow-selected -t', ['', '']), ('repeat-command', ['.']), + ('pin', ['']), + ('unpin', ['']), ])), ('insert', collections.OrderedDict([ diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index c3a3a8e34..d7c844c77 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -205,6 +205,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['pin'] = tab.pin + return data def _save_tab(self, tab, active): @@ -332,6 +335,9 @@ class SessionManager(QObject): pos = histentry['scroll-pos'] user_data['scroll-pos'] = QPoint(pos['x'], pos['y']) + if 'pin' in histentry: + new_tab.pin = histentry['pin'] + active = histentry.get('active', False) url = QUrl.fromEncoded(histentry['url'].encode('ascii')) if 'original-url' in histentry: From 22133beb724ba8289d3c0c417d3aec4124d3aa73 Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 6 Nov 2016 18:24:33 +0100 Subject: [PATCH 02/56] Fix small bug because result was not declared --- qutebrowser/browser/commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3ee28493e..5511dfeed 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -225,6 +225,7 @@ class CommandDispatcher: count: The tab index to close, or None """ tab = self._cntwidget(count) + result = True if tab is None: return From 6d7a6db130367b82bb014ea4165e254191148951 Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 6 Nov 2016 19:04:32 +0100 Subject: [PATCH 03/56] Proper title and size for pinned tab As I'm using self.count() without taking in consideration the number of pinned tabs the end result is a lot of empty space. --- qutebrowser/mainwindow/tabwidget.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 657a38dc3..be77f3631 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -107,7 +107,12 @@ class TabWidget(QTabWidget): fields['index'] = idx + 1 fmt = config.get('tabs', 'title-format') - title = '' if fmt is None else fmt.format(**fields) + + if fields['pin'] is True: + title = '{index}'.format(**fields) + else: + title = '' if fmt is None else fmt.format(**fields) + self.tabBar().setTabText(idx, title) def get_tab_fields(self, idx): @@ -120,6 +125,7 @@ class TabWidget(QTabWidget): fields['title'] = page_title fields['title_sep'] = ' - ' if page_title else '' fields['perc_raw'] = tab.progress() + fields['pin'] = tab.pin if tab.load_status() == usertypes.LoadStatus.loading: fields['perc'] = '[{}%] '.format(tab.progress()) @@ -439,11 +445,21 @@ class TabBar(QTabBar): # get scroll buttons as soon as needed. size = minimum_size else: + #TODO: relative size and/or configured one + tab = objreg.get('tab', scope='tab', window=self._win_id, tab=index) + if tab.pin is True: + size = QSize(40, height) + qtutils.ensure_valid(size) + return size # If we *do* have enough space, tabs should occupy the whole window # width. + #looks like this generates high cpu usage + #need to register the number of pin tabs in advance + #nb_of_pins = len([None for item in range(self.count()) if objreg.get('tab', scope='tab', window=self._win_id, tab=item).pin is True]) + #width = (self.width() + 40*nb_of_pins) / self.count() 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 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(): width += 1 size = QSize(width, height) From d592651c504b57901b5c33c23041da9063a3a80b Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 6 Nov 2016 23:24:24 +0100 Subject: [PATCH 04/56] Change command from pin/unpin to tab-pin --- qutebrowser/browser/commands.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 5511dfeed..4fb076d80 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -247,28 +247,22 @@ class CommandDispatcher: self._tabbed_browser.close_tab(tab) tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) - @cmdutils.register(instance='command-dispatcher', scope='window', name='pin') + @cmdutils.register(instance='command-dispatcher', scope='window', name='tab-pin') @cmdutils.argument('index') @cmdutils.argument('count', count=True) - def tab_pin(self, index=1, count=None): + def tab_pin(self, index=None, count=None): tab = self._cntwidget(count) if tab is None: return - tab.pin = True - self.tab_move(int(index)) - @cmdutils.register(instance='command-dispatcher', scope='window', name='unpin') - @cmdutils.argument('index') - @cmdutils.argument('count', count=True) - def tab_unpin(self, index=None, count=None): - tab = self._cntwidget(count) - if tab is None: - return - tab.pin = False - if index is not None: - self.tab_move(int(index)) + tab.pin = not tab.pin + + if tab.pin is True: + index = 1 if index is None else int(index) else: - self.tab_move(self._count()) + index = self._count() if index is None else int(index) + + self.tab_move(index) @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') From d7a1a542b6ccc81badc8ea871c5f1c1a2c20325d Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 6 Nov 2016 23:25:36 +0100 Subject: [PATCH 05/56] Change shortcut to tab-pin --- qutebrowser/config/configdata.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 3219b5c9b..6664e4b8e 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1652,8 +1652,7 @@ KEY_DATA = collections.OrderedDict([ ('follow-selected', RETURN_KEYS), ('follow-selected -t', ['', '']), ('repeat-command', ['.']), - ('pin', ['']), - ('unpin', ['']), + ('tab-pin', ['']), ])), ('insert', collections.OrderedDict([ From 29d1c0d68b48608ba577873935dd9751d5bd3548 Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 6 Nov 2016 23:27:06 +0100 Subject: [PATCH 06/56] Small fix for situations where we cannot find the tab for the index Need to investigate better why and when this is excatly happening --- qutebrowser/mainwindow/tabwidget.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index be77f3631..4f1d45842 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -446,11 +446,14 @@ class TabBar(QTabBar): size = minimum_size else: #TODO: relative size and/or configured one - tab = objreg.get('tab', scope='tab', window=self._win_id, tab=index) - if tab.pin is True: - size = QSize(40, height) - qtutils.ensure_valid(size) - return size + try: + tab = objreg.get('tab', scope='tab', window=self._win_id, tab=index) + if tab.pin is True: + size = QSize(40, height) + qtutils.ensure_valid(size) + return size + except KeyError: + pass # If we *do* have enough space, tabs should occupy the whole window # width. #looks like this generates high cpu usage From f8dffb4e5c56b663eb4678ee82aa56043899377d Mon Sep 17 00:00:00 2001 From: thuck Date: Mon, 7 Nov 2016 08:02:25 +0100 Subject: [PATCH 07/56] Some modifications from initial feedback Moved pin information from BrowserTab to TabData. Changed attribute from pin to pinned. Changed "ifs" to implicit check boolen value. Removed blancked line on before else statement. --- qutebrowser/browser/browsertab.py | 3 ++- qutebrowser/browser/commands.py | 15 ++++++++++----- qutebrowser/mainwindow/tabwidget.py | 4 ++-- qutebrowser/misc/sessions.py | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 67d2bee44..843447825 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -86,6 +86,7 @@ class TabData: viewing_source: Set if we're currently showing a source view. open_target: How the next clicked link should be opened. override_target: Override for open_target for fake clicks (like hints). + pinned: Flag to pin the tab """ def __init__(self): @@ -94,6 +95,7 @@ class TabData: self.inspector = None self.open_target = usertypes.ClickTarget.normal self.override_target = None + self.pinned = False def combined_target(self): if self.override_target is not None: @@ -560,7 +562,6 @@ class AbstractTab(QWidget): self._mouse_event_filter = mouse.MouseEventFilter( self, widget_class=self.WIDGET_CLASS, parent=self) self.backend = None - self.pin = False # FIXME:qtwebengine Should this be public api via self.hints? # Also, should we get it out of objreg? diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 4fb076d80..d6ab0771a 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -229,7 +229,7 @@ class CommandDispatcher: if tab is None: return - if tab.pin is True: + if tab.data.pinned: result = message.ask("Are you sure you want to close a pinned tab?", mode=usertypes.PromptMode.yesno, default=False) @@ -251,13 +251,19 @@ class CommandDispatcher: @cmdutils.argument('index') @cmdutils.argument('count', count=True) def tab_pin(self, index=None, count=None): + """Pin/Unpin the current tab. + + Args: + index: Location where the tab should be pinned/unpinned. + count: The tab index to pin or unpin + """ tab = self._cntwidget(count) if tab is None: return - tab.pin = not tab.pin + tab.data.pinned = not tab.data.pinned - if tab.pin is True: + if tab.data.pinned: index = 1 if index is None else int(index) else: index = self._count() if index is None else int(index) @@ -307,9 +313,8 @@ class CommandDispatcher: else: # Explicit count with a tab that doesn't exist. return - elif curtab.pin is True: + elif curtab.data.pinned: message.info("Tab is pinned!") - else: curtab.openurl(cur_url) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 4f1d45842..a6d56014b 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -125,7 +125,7 @@ class TabWidget(QTabWidget): fields['title'] = page_title fields['title_sep'] = ' - ' if page_title else '' fields['perc_raw'] = tab.progress() - fields['pin'] = tab.pin + fields['pin'] = tab.data.pinned if tab.load_status() == usertypes.LoadStatus.loading: fields['perc'] = '[{}%] '.format(tab.progress()) @@ -448,7 +448,7 @@ class TabBar(QTabBar): #TODO: relative size and/or configured one try: tab = objreg.get('tab', scope='tab', window=self._win_id, tab=index) - if tab.pin is True: + if tab.data.pinned: size = QSize(40, height) qtutils.ensure_valid(size) return size diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index d7c844c77..ce1582413 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -206,7 +206,7 @@ class SessionManager(QObject): pos = user_data['scroll-pos'] data['scroll-pos'] = {'x': pos.x(), 'y': pos.y()} - data['pin'] = tab.pin + data['pin'] = tab.data.pinned return data @@ -336,7 +336,7 @@ class SessionManager(QObject): user_data['scroll-pos'] = QPoint(pos['x'], pos['y']) if 'pin' in histentry: - new_tab.pin = histentry['pin'] + new_tab.data.pinned = histentry['pin'] active = histentry.get('active', False) url = QUrl.fromEncoded(histentry['url'].encode('ascii')) From 20eae4d671642413aa5008ae1d3e185d69b89a10 Mon Sep 17 00:00:00 2001 From: thuck Date: Mon, 7 Nov 2016 08:11:47 +0100 Subject: [PATCH 08/56] Modifed exception structure --- qutebrowser/mainwindow/tabwidget.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index a6d56014b..bfdf53e45 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -448,12 +448,14 @@ class TabBar(QTabBar): #TODO: relative size and/or configured one try: tab = objreg.get('tab', scope='tab', window=self._win_id, tab=index) + except KeyError: + pass + else: if tab.data.pinned: size = QSize(40, height) qtutils.ensure_valid(size) return size - except KeyError: - pass + # If we *do* have enough space, tabs should occupy the whole window # width. #looks like this generates high cpu usage From 4ed046d5e71811da7659a820c4b5882935c06ddc Mon Sep 17 00:00:00 2001 From: thuck Date: Mon, 7 Nov 2016 21:12:34 +0100 Subject: [PATCH 09/56] Everything is pinned instead of pin, and one if corrected --- qutebrowser/browser/commands.py | 2 +- qutebrowser/mainwindow/tabwidget.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index d6ab0771a..c3de57387 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -255,7 +255,7 @@ class CommandDispatcher: Args: index: Location where the tab should be pinned/unpinned. - count: The tab index to pin or unpin + count: The tab index to pin or unpin, or None """ tab = self._cntwidget(count) if tab is None: diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index bfdf53e45..8d99d5596 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -108,7 +108,7 @@ class TabWidget(QTabWidget): fmt = config.get('tabs', 'title-format') - if fields['pin'] is True: + if fields['pinned']: title = '{index}'.format(**fields) else: title = '' if fmt is None else fmt.format(**fields) @@ -125,7 +125,7 @@ class TabWidget(QTabWidget): fields['title'] = page_title fields['title_sep'] = ' - ' if page_title else '' fields['perc_raw'] = tab.progress() - fields['pin'] = tab.data.pinned + fields['pinned'] = tab.data.pinned if tab.load_status() == usertypes.LoadStatus.loading: fields['perc'] = '[{}%] '.format(tab.progress()) From ec50d395781cd99c6e7b8ff95c220aed7a18e2ee Mon Sep 17 00:00:00 2001 From: thuck Date: Mon, 7 Nov 2016 21:25:05 +0100 Subject: [PATCH 10/56] Some fixes for the pylint --- qutebrowser/browser/commands.py | 5 +++-- qutebrowser/mainwindow/tabwidget.py | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index c3de57387..11dbb5288 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -247,7 +247,8 @@ class CommandDispatcher: self._tabbed_browser.close_tab(tab) tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) - @cmdutils.register(instance='command-dispatcher', scope='window', name='tab-pin') + @cmdutils.register(instance='command-dispatcher', scope='window', + name='tab-pin') @cmdutils.argument('index') @cmdutils.argument('count', count=True) def tab_pin(self, index=None, count=None): @@ -267,7 +268,7 @@ class CommandDispatcher: index = 1 if index is None else int(index) else: index = self._count() if index is None else int(index) - + self.tab_move(index) @cmdutils.register(instance='command-dispatcher', name='open', diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 8d99d5596..ac9c0ede3 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -447,7 +447,8 @@ class TabBar(QTabBar): else: #TODO: relative size and/or configured one try: - tab = objreg.get('tab', scope='tab', window=self._win_id, tab=index) + tab = objreg.get('tab', scope='tab', + window=self._win_id, tab=index) except KeyError: pass else: @@ -460,7 +461,9 @@ class TabBar(QTabBar): # width. #looks like this generates high cpu usage #need to register the number of pin tabs in advance - #nb_of_pins = len([None for item in range(self.count()) if objreg.get('tab', scope='tab', window=self._win_id, tab=item).pin is True]) + #nb_of_pins = len([None for item in range(self.count()) + # if objreg.get('tab', scope='tab', + # window=self._win_id, tab=item).pin is True]) #width = (self.width() + 40*nb_of_pins) / self.count() width = self.width() / self.count() ## If width is not divisible by count, add a pixel to some tabs so From f10284b04afa39994cc7fd2ad240155470a8f027 Mon Sep 17 00:00:00 2001 From: thuck Date: Mon, 7 Nov 2016 22:28:05 +0100 Subject: [PATCH 11/56] Initial work on message.confirm_async Creation of _tab_close and usage of partial. --- qutebrowser/browser/commands.py | 37 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 11dbb5288..32eb80c69 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -212,6 +212,19 @@ class CommandDispatcher: "{!r}!".format(conf_selection)) return None + def _tab_close(self, tab, left=False, right=False, opposite=False, count=None): + tabbar = self._tabbed_browser.tabBar() + selection_override = self._get_selection_override(left, right, + opposite) + if selection_override is None: + self._tabbed_browser.close_tab(tab) + else: + old_selection_behavior = tabbar.selectionBehaviorOnRemove() + tabbar.setSelectionBehaviorOnRemove(selection_override) + self._tabbed_browser.close_tab(tab) + tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) + + @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) def tab_close(self, left=False, right=False, opposite=False, count=None): @@ -229,23 +242,17 @@ class CommandDispatcher: if tab is None: return + close = functools.partial(self._tab_close, tab, left, + right, opposite, count) + if tab.data.pinned: - result = message.ask("Are you sure you want to close a pinned tab?", - mode=usertypes.PromptMode.yesno, default=False) - - if result is False or result is None: - return - - tabbar = self._tabbed_browser.tabBar() - selection_override = self._get_selection_override(left, right, - opposite) - if selection_override is None: - self._tabbed_browser.close_tab(tab) + message.confirm_async(title='Pinned Tab', + text="Are you sure you want to close a pinned tab?", + yes_action=close, default=False) else: - old_selection_behavior = tabbar.selectionBehaviorOnRemove() - tabbar.setSelectionBehaviorOnRemove(selection_override) - self._tabbed_browser.close_tab(tab) - tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) + close() + + @cmdutils.register(instance='command-dispatcher', scope='window', name='tab-pin') From f9b1d998d43cb0c4ea2eb887389d0489b9d29ffb Mon Sep 17 00:00:00 2001 From: thuck Date: Mon, 7 Nov 2016 22:32:42 +0100 Subject: [PATCH 12/56] Last configuration as pin changed to pinned --- qutebrowser/misc/sessions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index ce1582413..f6c6d97b2 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -206,7 +206,7 @@ class SessionManager(QObject): pos = user_data['scroll-pos'] data['scroll-pos'] = {'x': pos.x(), 'y': pos.y()} - data['pin'] = tab.data.pinned + data['pinned'] = tab.data.pinned return data @@ -335,8 +335,8 @@ class SessionManager(QObject): pos = histentry['scroll-pos'] user_data['scroll-pos'] = QPoint(pos['x'], pos['y']) - if 'pin' in histentry: - new_tab.data.pinned = histentry['pin'] + if 'pinned' in histentry: + new_tab.data.pinned = histentry['pinned'] active = histentry.get('active', False) url = QUrl.fromEncoded(histentry['url'].encode('ascii')) From b24ac0ae785cd3d7aa8bd53eef8de934c04d0e7a Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 8 Nov 2016 04:45:07 +0100 Subject: [PATCH 13/56] More small fixes Removed unsed variables. Removed some empty lines. Inncluded docstring. --- qutebrowser/browser/commands.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 32eb80c69..76e1075a1 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -212,7 +212,18 @@ class CommandDispatcher: "{!r}!".format(conf_selection)) return None - def _tab_close(self, tab, left=False, right=False, opposite=False, count=None): + def _tab_close(self, tab, left=False, right=False, opposite=False): + """Helper function for tab_close be able to handle message.async. + + Args: + tab: Tab select to be closed. + left: Force selecting the tab to the left of the current tab. + right: Force selecting the tab to the right of 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 + + """ tabbar = self._tabbed_browser.tabBar() selection_override = self._get_selection_override(left, right, opposite) @@ -224,7 +235,6 @@ class CommandDispatcher: self._tabbed_browser.close_tab(tab) tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) - @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) def tab_close(self, left=False, right=False, opposite=False, count=None): @@ -238,12 +248,11 @@ class CommandDispatcher: count: The tab index to close, or None """ tab = self._cntwidget(count) - result = True if tab is None: return close = functools.partial(self._tab_close, tab, left, - right, opposite, count) + right, opposite) if tab.data.pinned: message.confirm_async(title='Pinned Tab', @@ -252,8 +261,6 @@ class CommandDispatcher: else: close() - - @cmdutils.register(instance='command-dispatcher', scope='window', name='tab-pin') @cmdutils.argument('index') From 4f0034911a49c0c4f4ff06fe013666e649e8f2cb Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 8 Nov 2016 07:56:13 +0100 Subject: [PATCH 14/56] title-format-pinned initial work Created configuration configdata. Load and use template defined on configdata. TODO: ability to conserve information between restart TODO: ability to update title on configuration change --- qutebrowser/config/configdata.py | 17 +++++++++++++++++ qutebrowser/mainwindow/tabwidget.py | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 6664e4b8e..1dadb4fb1 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -679,6 +679,23 @@ def data(readonly=False): "* `{scroll_pos}`: The page scroll position.\n" "* `{host}`: The host of the current web page."), + ('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 pinned 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 web page\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.\n" + "* `{scroll_pos}`: The page scroll position.\n" + "* `{host}`: The host of the current web page."), + ('title-alignment', SettingValue(typ.TextAlignment(), 'left'), "Alignment of the text inside of tabs"), diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index ac9c0ede3..e22c7a75f 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -107,9 +107,10 @@ class TabWidget(QTabWidget): fields['index'] = idx + 1 fmt = config.get('tabs', 'title-format') + fmt_pinned = config.get('tabs', 'title-format-pinned') if fields['pinned']: - title = '{index}'.format(**fields) + title = fmt_pinned.format(**fields) else: title = '' if fmt is None else fmt.format(**fields) From 931b008f892e4d6541bca3a0982635973be8680b Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 8 Nov 2016 08:12:40 +0100 Subject: [PATCH 15/56] Update title when title-format-pinned is modified --- qutebrowser/mainwindow/tabbedbrowser.py | 1 + qutebrowser/mainwindow/tabwidget.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 6efec0851..a7083b36a 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -120,6 +120,7 @@ class TabbedBrowser(tabwidget.TabWidget): 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) + objreg.get('config').changed.connect(self.update_tab_titles_pinned) def __repr__(self): return utils.get_repr(self, count=self.count()) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index e22c7a75f..3d4151fbd 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -157,6 +157,12 @@ class TabWidget(QTabWidget): for idx in range(self.count()): self.update_tab_title(idx) + @config.change_filter('tabs', 'title-format-pinned') + def update_tab_titles_pinned(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) From 6f8aaccc2b0ba365f620494b22ed9093ddfc91a9 Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 8 Nov 2016 21:12:20 +0100 Subject: [PATCH 16/56] Attach pin information to tabwidget Simple access to pin information on tab widget. Some change for the fmt_pin to not use fields as cheap trick --- qutebrowser/browser/commands.py | 1 + qutebrowser/mainwindow/tabwidget.py | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 76e1075a1..4462e56e2 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -284,6 +284,7 @@ class CommandDispatcher: index = self._count() if index is None else int(index) self.tab_move(index) + self._tabbed_browser.set_tab_pinned(self._current_index(), tab.data.pinned) @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 3d4151fbd..b42ff0f3f 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -91,6 +91,17 @@ class TabWidget(QTabWidget): bar.set_tab_data(idx, 'indicator-color', color) bar.update(bar.tabRect(idx)) + def set_tab_pinned(self, idx, pinned): + """Set the tab status as pinned. + + Args: + idx: The tab index. + pinned: Pinned tab state. + """ + bar = self.tabBar() + bar.set_tab_data(idx, 'pinned',pinned) + 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) @@ -102,6 +113,7 @@ 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 @@ -109,8 +121,8 @@ class TabWidget(QTabWidget): fmt = config.get('tabs', 'title-format') fmt_pinned = config.get('tabs', 'title-format-pinned') - if fields['pinned']: - title = fmt_pinned.format(**fields) + 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) @@ -126,7 +138,10 @@ class TabWidget(QTabWidget): fields['title'] = page_title fields['title_sep'] = ' - ' if page_title else '' fields['perc_raw'] = tab.progress() - fields['pinned'] = tab.data.pinned + + #TODO: Move this to a proper place + if tab.data.pinned: + self.set_tab_pinned(idx, tab.data.pinned) if tab.load_status() == usertypes.LoadStatus.loading: fields['perc'] = '[{}%] '.format(tab.progress()) @@ -454,12 +469,11 @@ class TabBar(QTabBar): else: #TODO: relative size and/or configured one try: - tab = objreg.get('tab', scope='tab', - window=self._win_id, tab=index) + pinned = self.tab_data(index, 'pinned') except KeyError: pass else: - if tab.data.pinned: + if pinned: size = QSize(40, height) qtutils.ensure_valid(size) return size From d7f5f61f03ff55fc58dbb18947a91f97b54669ac Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 9 Nov 2016 23:50:41 +0100 Subject: [PATCH 17/56] Implemented counter for total number of tabs With this counter we can better control the space on the tabbar. --- qutebrowser/browser/commands.py | 4 ++++ qutebrowser/mainwindow/tabwidget.py | 32 +++++++++++++++++------------ qutebrowser/misc/sessions.py | 2 ++ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 4462e56e2..1ead1e363 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -227,6 +227,10 @@ class CommandDispatcher: tabbar = self._tabbed_browser.tabBar() selection_override = self._get_selection_override(left, right, opposite) + + if tab.data.pinned: + tabbar.pinned -= 1 + if selection_override is None: self._tabbed_browser.close_tab(tab) else: diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index b42ff0f3f..c12b88952 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -99,9 +99,16 @@ class TabWidget(QTabWidget): pinned: Pinned tab state. """ bar = self.tabBar() - bar.set_tab_data(idx, 'pinned',pinned) + bar.set_tab_data(idx, 'pinned', pinned) bar.update(bar.tabRect(idx)) + if pinned: + bar.pinned += 1 + else: + bar.pinned -= 1 + + bar.refresh() + def set_page_title(self, idx, title): """Set the tab title user data.""" self.tabBar().set_tab_data(idx, 'page-title', title) @@ -139,10 +146,6 @@ class TabWidget(QTabWidget): fields['title_sep'] = ' - ' if page_title else '' fields['perc_raw'] = tab.progress() - #TODO: Move this to a proper place - if tab.data.pinned: - self.set_tab_pinned(idx, tab.data.pinned) - if tab.load_status() == usertypes.LoadStatus.loading: fields['perc'] = '[{}%] '.format(tab.progress()) else: @@ -298,6 +301,7 @@ class TabBar(QTabBar): self._auto_hide_timer.timeout.connect(self._tabhide) self.setAutoFillBackground(True) self.set_colors() + self.pinned = 0 config_obj.changed.connect(self.set_colors) QTimer.singleShot(0, self._tabhide) config_obj.changed.connect(self.on_tab_colors_changed) @@ -479,14 +483,16 @@ class TabBar(QTabBar): return size # If we *do* have enough space, tabs should occupy the whole window - # width. - #looks like this generates high cpu usage - #need to register the number of pin tabs in advance - #nb_of_pins = len([None for item in range(self.count()) - # if objreg.get('tab', scope='tab', - # window=self._win_id, tab=item).pin is True]) - #width = (self.width() + 40*nb_of_pins) / self.count() - width = self.width() / self.count() + # width. Also taken in consideration the reduced space necessary for + # the pinned tabs. + #TODO: During shutdown the self.count goes down, but the self.pinned not + #this generates some odd bahavior. + #To avoid this we compare self.count against self.pinned. + if self.pinned > 0 and self.count() > self.pinned: + width = (self.width() - 40*self.pinned) / (self.count() - self.pinned) + 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(): diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index f6c6d97b2..31335f33b 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -381,6 +381,8 @@ 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) if tab_to_focus is not None: tabbed_browser.setCurrentIndex(tab_to_focus) if win.get('active', False): From 9beb097c5398b59b3b0025b664fd2a55bf3cf598 Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 9 Nov 2016 23:52:56 +0100 Subject: [PATCH 18/56] Corrected some unecessary spaces --- qutebrowser/browser/commands.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 1ead1e363..0e43cbf17 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -220,9 +220,8 @@ class CommandDispatcher: left: Force selecting the tab to the left of the current tab. right: Force selecting the tab to the right of the current tab. opposite: Force selecting the tab in the opposite direction of - what's configured in 'tabs->select-on-remove'. + what's configured in 'tabs->select-on-remove'. count: The tab index to close, or None - """ tabbar = self._tabbed_browser.tabBar() selection_override = self._get_selection_override(left, right, From 19cc721eb111ba80eb592a385ba3a17e05ce7829 Mon Sep 17 00:00:00 2001 From: thuck Date: Fri, 11 Nov 2016 12:05:04 +0100 Subject: [PATCH 19/56] Changed behavior on location of tab being pinned Now when a tab is pinned it goes to the end of all pinned tabs. Before it went to the index 1. --- qutebrowser/browser/commands.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index d9122f869..3230daa46 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -271,6 +271,7 @@ class CommandDispatcher: index: Location where the tab should be pinned/unpinned. count: The tab index to pin or unpin, or None """ + tabbar = self._tabbed_browser.tabBar() tab = self._cntwidget(count) if tab is None: return @@ -278,7 +279,7 @@ class CommandDispatcher: tab.data.pinned = not tab.data.pinned if tab.data.pinned: - index = 1 if index is None else int(index) + index = tabbar.pinned + 1 if index is None else int(index) else: index = self._count() if index is None else int(index) From 25b69fe76ad5ae995dea071f29842b5ee780a494 Mon Sep 17 00:00:00 2001 From: thuck Date: Fri, 11 Nov 2016 13:57:01 +0100 Subject: [PATCH 20/56] Configuration for the size of a pinned tab --- qutebrowser/config/configdata.py | 5 +++++ qutebrowser/mainwindow/tabwidget.py | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index a5b21e8e2..cefda8857 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -654,6 +654,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 of the pinned tab if it's horizontal, in px."), + ('indicator-width', SettingValue(typ.Int(minval=0), '3'), "Width of the progress indicator (0 to disable)."), diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 50ab0af92..484cd8b5d 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -185,6 +185,11 @@ class TabWidget(QTabWidget): for idx in range(self.count()): self.update_tab_title(idx) + @config.change_filter('tabs', 'pinned-width') + def update_tab_pinned_width(self): + """Refresh bar""" + self.tabBar().refresh() + def tabInserted(self, idx): """Update titles when a tab was inserted.""" super().tabInserted(idx) @@ -482,25 +487,27 @@ class TabBar(QTabBar): # get scroll buttons as soon as needed. size = minimum_size else: - #TODO: relative size and/or configured one + tab_width_pinned_conf = config.get('tabs', 'pinned-width') + try: pinned = self.tab_data(index, 'pinned') except KeyError: pass else: if pinned: - size = QSize(40, height) + 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. Also taken in consideration the reduced space necessary for # the pinned tabs. - #TODO: During shutdown the self.count goes down, but the self.pinned not + #WORKAROUND: During shutdown the self.count goes down, but the self.pinned not #this generates some odd bahavior. #To avoid this we compare self.count against self.pinned. if self.pinned > 0 and self.count() > self.pinned: - width = (self.width() - 40*self.pinned) / (self.count() - self.pinned) + pinned_width = tab_width_pinned_conf * self.pinned + width = (self.width() - pinned_width) / (self.count() - self.pinned) else: width = self.width() / self.count() From 9eb0a85bae3ae7cbc2d199fbce106fdc8f03541b Mon Sep 17 00:00:00 2001 From: thuck Date: Fri, 11 Nov 2016 17:10:46 +0100 Subject: [PATCH 21/56] Some fixes for pyflake, pylint and remove useless function --- qutebrowser/browser/commands.py | 7 +++++-- qutebrowser/mainwindow/tabwidget.py | 16 ++++++---------- tests/unit/mainwindow/test_tabwidget.py | 2 ++ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3230daa46..484e9ebeb 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -213,6 +213,7 @@ class CommandDispatcher: 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 select to be closed. prev: Force selecting the tab before the current tab. @@ -240,6 +241,7 @@ class CommandDispatcher: @cmdutils.argument('count', count=True) def tab_close(self, prev=False, next_=False, opposite=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. @@ -251,7 +253,7 @@ class CommandDispatcher: if tab is None: return close = functools.partial(self._tab_close, tab, prev, - next_, opposite) + next_, opposite) if tab.data.pinned: message.confirm_async(title='Pinned Tab', @@ -284,7 +286,8 @@ class CommandDispatcher: index = self._count() if index is None else int(index) self.tab_move(index) - self._tabbed_browser.set_tab_pinned(self._current_index(), tab.data.pinned) + self._tabbed_browser.set_tab_pinned(self._current_index(), + tab.data.pinned) @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 484cd8b5d..f89a0f5c6 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -185,11 +185,6 @@ class TabWidget(QTabWidget): for idx in range(self.count()): self.update_tab_title(idx) - @config.change_filter('tabs', 'pinned-width') - def update_tab_pinned_width(self): - """Refresh bar""" - self.tabBar().refresh() - def tabInserted(self, idx): """Update titles when a tab was inserted.""" super().tabInserted(idx) @@ -500,14 +495,15 @@ class TabBar(QTabBar): return size # If we *do* have enough space, tabs should occupy the whole window - # width. Also taken in consideration the reduced space necessary for - # the pinned tabs. - #WORKAROUND: During shutdown the self.count goes down, but the self.pinned not - #this generates some odd bahavior. + # width. Also taken in consideration the reduced space necessary + #for the pinned tabs. + #WORKAROUND: During shutdown the self.count goes down, + #but the self.pinned not this generates some odd bahavior. #To avoid this we compare self.count against self.pinned. if self.pinned > 0 and self.count() > self.pinned: pinned_width = tab_width_pinned_conf * self.pinned - width = (self.width() - pinned_width) / (self.count() - self.pinned) + no_pinned_width = self.width() - pinned_width + width = no_pinned_width / (self.count() - self.pinned) else: width = self.width() / self.count() diff --git a/tests/unit/mainwindow/test_tabwidget.py b/tests/unit/mainwindow/test_tabwidget.py index b9a95c810..4329f12db 100644 --- a/tests/unit/mainwindow/test_tabwidget.py +++ b/tests/unit/mainwindow/test_tabwidget.py @@ -46,6 +46,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': { From a8ccfe050d345734cd46e251b69f32599887da7c Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 13 Nov 2016 08:56:43 +0100 Subject: [PATCH 22/56] Remove unecessary empty line --- qutebrowser/browser/commands.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 484e9ebeb..35ceb7e01 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -279,7 +279,6 @@ class CommandDispatcher: return tab.data.pinned = not tab.data.pinned - if tab.data.pinned: index = tabbar.pinned + 1 if index is None else int(index) else: From 84c41c964b89940a884e515d6b7c4deeeb253292 Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 13 Nov 2016 09:40:07 +0100 Subject: [PATCH 23/56] First test for tab-pin --- tests/end2end/features/tabs.feature | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 1663dd8d7..cede08933 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -5,6 +5,18 @@ Feature: Tab management Given I clean up open tabs And I set tabs -> tabs-are-windows to false + # :tab-pin + + Scenario: :tab-pin + 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/3.txt (active) + - data/numbers/1.txt + - data/numbers/2.txt + # :tab-close Scenario: :tab-close From e2a6f97c07227b64b53225a2c3fed640dd126592 Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 16 Nov 2016 07:48:12 +0100 Subject: [PATCH 24/56] Initial tests --- tests/end2end/features/tabs.feature | 45 +++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index cede08933..2e8a00790 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -5,18 +5,6 @@ Feature: Tab management Given I clean up open tabs And I set tabs -> tabs-are-windows to false - # :tab-pin - - Scenario: :tab-pin - 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/3.txt (active) - - data/numbers/1.txt - - data/numbers/2.txt - # :tab-close Scenario: :tab-close @@ -1009,3 +997,36 @@ Feature: Tab management And I run :tab-close ;; tab-prev Then qutebrowser should quit And no crash should happen + + # :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/3.txt (active) + - data/numbers/1.txt + - data/numbers/2.txt + + Scenario: :tab-pin unpin + When I run :tab-pin + Then the following tabs should be open: + - data/numbers/1.txt + - data/numbers/2.txt + - data/numbers/3.txt (active) + + Scenario: :tab-pin to index 2 + When I run :tab-pin 2 + Then the following tabs should be open: + - data/numbers/1.txt + - data/numbers/3.txt (active) + - data/numbers/2.txt + + Scenario: :tab-pin unpin to index 1 + When I run :tab-pin 1 + Then the following tabs should be open: + - data/numbers/3.txt (active) + - data/numbers/1.txt + - data/numbers/2.txt From e514b0d58eeaf6f7de911ade7da0854c5c4a5769 Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 16 Nov 2016 08:18:08 +0100 Subject: [PATCH 25/56] Included --force option for tab-close This makes possible to close pinned tabs without any confirmation. --- qutebrowser/browser/commands.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 8dbe4fe86..567e45a9f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -239,7 +239,8 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) - def tab_close(self, prev=False, next_=False, opposite=False, count=None): + def tab_close(self, prev=False, next_=False, opposite=False, + force=False, count=None): """Close the current/[count]th tab. Args: @@ -247,6 +248,7 @@ class CommandDispatcher: 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) @@ -255,7 +257,7 @@ class CommandDispatcher: close = functools.partial(self._tab_close, tab, prev, next_, opposite) - if tab.data.pinned: + 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=close, default=False) From 41adafdec49262ec24c4d5b8a854d1eccf235500 Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 16 Nov 2016 08:19:21 +0100 Subject: [PATCH 26/56] Fix initial tests --- tests/end2end/features/conftest.py | 2 +- tests/end2end/features/tabs.feature | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 7dd42c2d0..517d77144 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -160,7 +160,7 @@ def clean_open_tabs(quteproc): 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-close --force') @bdd.given('pdfjs is available') diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 2e8a00790..546eae425 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1011,21 +1011,32 @@ Feature: Tab management - data/numbers/2.txt Scenario: :tab-pin unpin - When I run :tab-pin + 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 + 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) Scenario: :tab-pin to index 2 - When I run :tab-pin 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 2 Then the following tabs should be open: - data/numbers/1.txt - data/numbers/3.txt (active) - data/numbers/2.txt Scenario: :tab-pin unpin to index 1 - When I run :tab-pin 1 + 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 + And I run :tab-pin 1 Then the following tabs should be open: - data/numbers/3.txt (active) - data/numbers/1.txt From 69c82f85632ab13c4c3e1675583874944d3f7286 Mon Sep 17 00:00:00 2001 From: thuck Date: Fri, 18 Nov 2016 07:42:48 +0100 Subject: [PATCH 27/56] Including tests for pinned tab prompt Duplicate function for "I wait for a prompt" --- tests/end2end/features/tabs.feature | 23 +++++++++++++++++++++++ tests/end2end/features/test_tabs_bdd.py | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 546eae425..c4c1d56e4 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1041,3 +1041,26 @@ Feature: Tab management - data/numbers/3.txt (active) - data/numbers/1.txt - data/numbers/2.txt + + Scenario: :tab-pin 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 a prompt + And I run :prompt-accept yes + Then the following tabs should be open: + - data/numbers/1.txt (active) + + Scenario: :tab-pin 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 a prompt + And I run :prompt-accept no + Then the following tabs should be open: + - data/numbers/1.txt + - data/numbers/2.txt (active) diff --git a/tests/end2end/features/test_tabs_bdd.py b/tests/end2end/features/test_tabs_bdd.py index bcae6d60d..a61dd4fcd 100644 --- a/tests/end2end/features/test_tabs_bdd.py +++ b/tests/end2end/features/test_tabs_bdd.py @@ -19,3 +19,7 @@ import pytest_bdd as bdd bdd.scenarios('tabs.feature') + +@bdd.when("I wait for a prompt") +def wait_for_prompt(quteproc): + quteproc.wait_for(message='Asking question *') From 175744761b7bdf66f797912167dc62da88954286 Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 22 Nov 2016 06:57:00 +0100 Subject: [PATCH 28/56] flake8 fixes --- qutebrowser/browser/commands.py | 2 +- tests/end2end/features/test_tabs_bdd.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 567e45a9f..8f71fe75a 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -240,7 +240,7 @@ class CommandDispatcher: @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): + force=False, count=None): """Close the current/[count]th tab. Args: diff --git a/tests/end2end/features/test_tabs_bdd.py b/tests/end2end/features/test_tabs_bdd.py index a61dd4fcd..d53241288 100644 --- a/tests/end2end/features/test_tabs_bdd.py +++ b/tests/end2end/features/test_tabs_bdd.py @@ -20,6 +20,7 @@ import pytest_bdd as bdd bdd.scenarios('tabs.feature') + @bdd.when("I wait for a prompt") def wait_for_prompt(quteproc): quteproc.wait_for(message='Asking question *') From 982e4f46e0d0dbd2887b203b3ecf2a402f42a0f5 Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 22 Nov 2016 07:24:02 +0100 Subject: [PATCH 29/56] Test for accidental url opened in a pinned tab --- tests/end2end/features/tabs.feature | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index c4c1d56e4..a2e903001 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1064,3 +1064,11 @@ Feature: Tab management Then the following tabs should be open: - data/numbers/1.txt - data/numbers/2.txt (active) + + Scenario: :tab-pin open url + When I open data/numbers/1.txt + And I run :tab-pin + And I run :open data/numbers/2.txt + Then the message "Tab is pinned!" should be shown + And the following tabs should be open: + - data/numbers/1.txt (active) From 92e1181680eeca0f4d6b73b541b1cd64e0a93ec6 Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 16 Nov 2016 08:18:08 +0100 Subject: [PATCH 30/56] Included --force option for tab-close This makes possible to close pinned tabs without any confirmation. --- qutebrowser/browser/commands.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 695ec1eff..0eb84c37b 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -239,7 +239,8 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) - def tab_close(self, prev=False, next_=False, opposite=False, count=None): + def tab_close(self, prev=False, next_=False, opposite=False, + force=False, count=None): """Close the current/[count]th tab. Args: @@ -247,6 +248,7 @@ class CommandDispatcher: 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) @@ -255,7 +257,7 @@ class CommandDispatcher: close = functools.partial(self._tab_close, tab, prev, next_, opposite) - if tab.data.pinned: + 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=close, default=False) From 9547938f7906b536f911f5e6cbbd7d98f17a4ffc Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 16 Nov 2016 08:19:21 +0100 Subject: [PATCH 31/56] Fix initial tests --- tests/end2end/features/conftest.py | 2 +- tests/end2end/features/tabs.feature | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 7dd42c2d0..517d77144 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -160,7 +160,7 @@ def clean_open_tabs(quteproc): 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-close --force') @bdd.given('pdfjs is available') diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 2e8a00790..546eae425 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1011,21 +1011,32 @@ Feature: Tab management - data/numbers/2.txt Scenario: :tab-pin unpin - When I run :tab-pin + 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 + 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) Scenario: :tab-pin to index 2 - When I run :tab-pin 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 2 Then the following tabs should be open: - data/numbers/1.txt - data/numbers/3.txt (active) - data/numbers/2.txt Scenario: :tab-pin unpin to index 1 - When I run :tab-pin 1 + 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 + And I run :tab-pin 1 Then the following tabs should be open: - data/numbers/3.txt (active) - data/numbers/1.txt From be980a7268d5fb54173600e7addc8bed27ae24be Mon Sep 17 00:00:00 2001 From: thuck Date: Fri, 18 Nov 2016 07:42:48 +0100 Subject: [PATCH 32/56] Including tests for pinned tab prompt Duplicate function for "I wait for a prompt" --- tests/end2end/features/tabs.feature | 23 +++++++++++++++++++++++ tests/end2end/features/test_tabs_bdd.py | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 546eae425..c4c1d56e4 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1041,3 +1041,26 @@ Feature: Tab management - data/numbers/3.txt (active) - data/numbers/1.txt - data/numbers/2.txt + + Scenario: :tab-pin 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 a prompt + And I run :prompt-accept yes + Then the following tabs should be open: + - data/numbers/1.txt (active) + + Scenario: :tab-pin 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 a prompt + And I run :prompt-accept no + Then the following tabs should be open: + - data/numbers/1.txt + - data/numbers/2.txt (active) diff --git a/tests/end2end/features/test_tabs_bdd.py b/tests/end2end/features/test_tabs_bdd.py index bcae6d60d..a61dd4fcd 100644 --- a/tests/end2end/features/test_tabs_bdd.py +++ b/tests/end2end/features/test_tabs_bdd.py @@ -19,3 +19,7 @@ import pytest_bdd as bdd bdd.scenarios('tabs.feature') + +@bdd.when("I wait for a prompt") +def wait_for_prompt(quteproc): + quteproc.wait_for(message='Asking question *') From 9dff4299e89c4edba2d7f2909c7c05658df57737 Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 22 Nov 2016 06:57:00 +0100 Subject: [PATCH 33/56] flake8 fixes --- qutebrowser/browser/commands.py | 2 +- tests/end2end/features/test_tabs_bdd.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 0eb84c37b..ef3d0919e 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -240,7 +240,7 @@ class CommandDispatcher: @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): + force=False, count=None): """Close the current/[count]th tab. Args: diff --git a/tests/end2end/features/test_tabs_bdd.py b/tests/end2end/features/test_tabs_bdd.py index a61dd4fcd..d53241288 100644 --- a/tests/end2end/features/test_tabs_bdd.py +++ b/tests/end2end/features/test_tabs_bdd.py @@ -20,6 +20,7 @@ import pytest_bdd as bdd bdd.scenarios('tabs.feature') + @bdd.when("I wait for a prompt") def wait_for_prompt(quteproc): quteproc.wait_for(message='Asking question *') From 05d36317501ec440cafce6c32ce8160b2e885b4e Mon Sep 17 00:00:00 2001 From: thuck Date: Tue, 22 Nov 2016 07:24:02 +0100 Subject: [PATCH 34/56] Test for accidental url opened in a pinned tab --- tests/end2end/features/tabs.feature | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index c4c1d56e4..a2e903001 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1064,3 +1064,11 @@ Feature: Tab management Then the following tabs should be open: - data/numbers/1.txt - data/numbers/2.txt (active) + + Scenario: :tab-pin open url + When I open data/numbers/1.txt + And I run :tab-pin + And I run :open data/numbers/2.txt + Then the message "Tab is pinned!" should be shown + And the following tabs should be open: + - data/numbers/1.txt (active) From e9c79e9be3549e7547ea9130fa0ad5ae7818169a Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 23 Nov 2016 08:18:10 +0100 Subject: [PATCH 35/56] Fix for comments on configdata --- qutebrowser/config/configdata.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 9b60b7402..53378daea 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -657,7 +657,7 @@ def data(readonly=False): ('pinned-width', SettingValue(typ.Int(minval=10), '43'), - "The width of the pinned tab if it's horizontal, in px."), + "The width for pinned tabs with a horizontal tabbar, in px."), ('indicator-width', SettingValue(typ.Int(minval=0), '3'), @@ -690,17 +690,8 @@ def data(readonly=False): fields=['perc', 'perc_raw', 'title', 'title_sep', 'index', 'id', 'scroll_pos', 'host'], none_ok=True), '{index}'), - "The format to use for the pinned 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 web page\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.\n" - "* `{scroll_pos}`: The page scroll position.\n" - "* `{host}`: The host of the current web page."), + "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'), From 8d4b55bb802d286c70f50d40dbb443b55952a02a Mon Sep 17 00:00:00 2001 From: thuck Date: Wed, 23 Nov 2016 22:18:55 +0100 Subject: [PATCH 36/56] Fix comments and change self.pinned to self.pinned_count --- qutebrowser/browser/commands.py | 2 +- qutebrowser/mainwindow/tabwidget.py | 38 ++++++++++++++--------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index ef3d0919e..e258ce135 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -282,7 +282,7 @@ class CommandDispatcher: tab.data.pinned = not tab.data.pinned if tab.data.pinned: - index = tabbar.pinned + 1 if index is None else int(index) + index = tabbar.pinned_count + 1 if index is None else int(index) else: index = self._count() if index is None else int(index) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 187bfb956..b65f2c032 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -103,9 +103,9 @@ class TabWidget(QTabWidget): bar.update(bar.tabRect(idx)) if pinned: - bar.pinned += 1 + bar.pinned_count += 1 else: - bar.pinned -= 1 + bar.pinned_count -= 1 bar.refresh() @@ -306,7 +306,7 @@ class TabBar(QTabBar): self._auto_hide_timer.timeout.connect(self._tabhide) self.setAutoFillBackground(True) self.set_colors() - self.pinned = 0 + 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) @@ -488,28 +488,28 @@ class TabBar(QTabBar): try: pinned = self.tab_data(index, 'pinned') except KeyError: - pass - else: - if pinned: - size = QSize(tab_width_pinned_conf, height) - qtutils.ensure_valid(size) - return size + 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. Also taken in consideration the reduced space necessary - #for the pinned tabs. - #WORKAROUND: During shutdown the self.count goes down, - #but the self.pinned not this generates some odd bahavior. - #To avoid this we compare self.count against self.pinned. - if self.pinned > 0 and self.count() > self.pinned: - pinned_width = tab_width_pinned_conf * self.pinned + # width. If there is pinned tabs their size will be substracted 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) + 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 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(): width += 1 size = QSize(width, height) From a25409755886f5e478312cd7898e02375a733534 Mon Sep 17 00:00:00 2001 From: thuck Date: Thu, 24 Nov 2016 00:05:17 +0100 Subject: [PATCH 37/56] Using log instead of prompt functions for test --- tests/end2end/features/tabs.feature | 4 ++-- tests/end2end/features/test_tabs_bdd.py | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index a2e903001..5a7967b3a 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1048,7 +1048,7 @@ Feature: Tab management 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 a prompt + 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) @@ -1059,7 +1059,7 @@ Feature: Tab management 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 a prompt + 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 diff --git a/tests/end2end/features/test_tabs_bdd.py b/tests/end2end/features/test_tabs_bdd.py index d53241288..bcae6d60d 100644 --- a/tests/end2end/features/test_tabs_bdd.py +++ b/tests/end2end/features/test_tabs_bdd.py @@ -19,8 +19,3 @@ import pytest_bdd as bdd bdd.scenarios('tabs.feature') - - -@bdd.when("I wait for a prompt") -def wait_for_prompt(quteproc): - quteproc.wait_for(message='Asking question *') From 2eb07fc9cc363f2195a20af8261f4cd9a8ba55e7 Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 19 Mar 2017 14:03:24 +0100 Subject: [PATCH 38/56] Fix line size --- qutebrowser/mainwindow/tabwidget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index b65f2c032..29daca354 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -496,8 +496,8 @@ class TabBar(QTabBar): return size # If we *do* have enough space, tabs should occupy the whole window - # width. If there is pinned tabs their size will be substracted from - # the total window width. + # width. If there is pinned tabs their size will be substracted + # 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. From 02bf0401ab4c8247fa86935e8661a5e784f46a3e Mon Sep 17 00:00:00 2001 From: thuck Date: Sun, 19 Mar 2017 14:05:21 +0100 Subject: [PATCH 39/56] Last pinned to pinned_count --- qutebrowser/browser/commands.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index e258ce135..b0c8dc9e9 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -226,9 +226,6 @@ class CommandDispatcher: selection_override = self._get_selection_override(prev, next_, opposite) - if tab.data.pinned: - tabbar.pinned -= 1 - if selection_override is None: self._tabbed_browser.close_tab(tab) else: @@ -237,6 +234,9 @@ class CommandDispatcher: self._tabbed_browser.close_tab(tab) tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) + if tab.data.pinned: + tabbar.pinned_count -= 1 + @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) def tab_close(self, prev=False, next_=False, opposite=False, From 3317834b3626c41cbe803c4c22c5b4485e8b92ce Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Thu, 11 May 2017 13:28:26 -0700 Subject: [PATCH 40/56] Fix a bug where pinned tabs were occasionally miscounted Example case: :tab-only. This should cover other cases, but currently those cases (such as :tab-only) do NOT have a warning message when popping up. --- qutebrowser/browser/commands.py | 12 +++++------- qutebrowser/mainwindow/tabbedbrowser.py | 4 ++++ tests/end2end/features/conftest.py | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index f55e77c6f..2bc91c8b3 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -220,9 +220,6 @@ class CommandDispatcher: self._tabbed_browser.close_tab(tab) tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) - if tab.data.pinned: - tabbar.pinned_count -= 1 - @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) def tab_close(self, prev=False, next_=False, opposite=False, @@ -241,12 +238,13 @@ class CommandDispatcher: if tab is None: return close = functools.partial(self._tab_close, tab, prev, - next_, opposite) + next_, opposite) 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=close, default=False) + message.confirm_async( + title='Pinned Tab', + text="Are you sure you want to close a pinned tab?", + yes_action=close, default=False) else: close() diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 6bce2166a..41a7d00df 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -241,6 +241,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. diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 2aa897dfa..3b6d86c3d 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -163,6 +163,7 @@ def clean_open_tabs(quteproc): quteproc.send_cmd(':tab-close --force') quteproc.wait_for_load_finished_url('about:blank') + @bdd.given('pdfjs is available') def pdfjs_available(): if not pdfjs.is_available(): From cb654225fd1577e4605a08c644a056f37f368cbf Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Thu, 11 May 2017 14:05:25 -0700 Subject: [PATCH 41/56] Add a test case for loading/saving pinned tabs in sessions --- tests/end2end/features/sessions.feature | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/sessions.feature b/tests/end2end/features/sessions.feature index 226d3107d..d8c766632 100644 --- a/tests/end2end/features/sessions.feature +++ b/tests/end2end/features/sessions.feature @@ -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,26 @@ 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-prev + And I run :tab-pin + And I run :tab-next + And I run :session-save pin_session + And I run :tab-only + And I run :tab-close --force + And I run :session-load -c pin_session + And I run :tab-prev + And I run :open data/numbers/4.txt + And I wait 10s + Then the message "Tab is pinned!" should be shown + And the following tabs should be open: + - data/numbers/2.txt (active) + - data/numbers/1.txt + - data/numbers/3.txt From 4c28487fd0339c51829b6765ab232b5f8702fee8 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Thu, 11 May 2017 14:30:45 -0700 Subject: [PATCH 42/56] Warn user if pinned tab is closed via tab-only --- qutebrowser/browser/commands.py | 30 ++++++++++++++++++++----- tests/end2end/features/tabs.feature | 2 +- tests/end2end/features/test_tabs_bdd.py | 5 ----- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 2bc91c8b3..1780e0053 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -852,7 +852,7 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) - def zoom(self, zoom: int = None, count=None): + def zoom(self, zoom: int=None, count=None): """Set the zoom level for the current tab. The zoom can be given as argument or as [count]. If neither is @@ -875,22 +875,40 @@ 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(index): + """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: + # Show alert only once, then exit + message.confirm_async( + title='Closing Pinned Tab', + text="This action will close at least one " + + "pinned tab, continue?", + yes_action=lambda: self.tab_only( + prev=prev, next_=next_, force=True), default=False) + # We will get called again with force if user selects yes + 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') diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 9e14e6ca6..de1c02bcc 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1077,7 +1077,7 @@ Feature: Tab management 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 a prompt + And I wait for "Asking question *" in the log And I run :prompt-accept yes Then the following tabs should be open: - data/numbers/1.txt (active) diff --git a/tests/end2end/features/test_tabs_bdd.py b/tests/end2end/features/test_tabs_bdd.py index 3b106d7b8..e86204ba9 100644 --- a/tests/end2end/features/test_tabs_bdd.py +++ b/tests/end2end/features/test_tabs_bdd.py @@ -19,8 +19,3 @@ import pytest_bdd as bdd bdd.scenarios('tabs.feature') - - -@bdd.when("I wait for a prompt") -def wait_for_prompt(quteproc): - quteproc.wait_for(message='Asking question *') From 21455cf0e74a2423b5188432f31f0d43c0df91ef Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Thu, 11 May 2017 15:37:52 -0700 Subject: [PATCH 43/56] Clean up pinned tab alert logic should be a lot more reusable now --- qutebrowser/browser/commands.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 1780e0053..af2da6869 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -220,6 +220,19 @@ 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, @@ -240,13 +253,7 @@ class CommandDispatcher: close = functools.partial(self._tab_close, tab, prev, next_, opposite) - 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=close, default=False) - else: - close() + self._tab_close_prompt_if_pinned(tab, force, close) @cmdutils.register(instance='command-dispatcher', scope='window', name='tab-pin') @@ -897,14 +904,9 @@ class CommandDispatcher: if not force: for i, tab in enumerate(self._tabbed_browser.widgets()): if _to_close(i) and tab.data.pinned: - # Show alert only once, then exit - message.confirm_async( - title='Closing Pinned Tab', - text="This action will close at least one " + - "pinned tab, continue?", - yes_action=lambda: self.tab_only( - prev=prev, next_=next_, force=True), default=False) - # We will get called again with force if user selects yes + 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()): From 66dfb1b1c9021cfd7f2d4d3fbfd12390d898d66f Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Thu, 11 May 2017 16:20:57 -0700 Subject: [PATCH 44/56] Fix a bug with titles not being refreshed when pinning tabs --- qutebrowser/mainwindow/tabwidget.py | 6 +++--- tests/end2end/features/conftest.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index b8eb8a226..70f1b5088 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -99,10 +99,11 @@ class TabWidget(QTabWidget): Args: idx: The tab index. - pinned: Pinned tab state. + pinned: Pinned tab state to set. """ bar = self.tabBar() bar.set_tab_data(idx, 'pinned', pinned) + self.update_tab_title(idx) bar.update(bar.tabRect(idx)) if pinned: @@ -189,8 +190,7 @@ class TabWidget(QTabWidget): @config.change_filter('tabs', 'title-format-pinned') def update_tab_titles_pinned(self): """Update all texts.""" - for idx in range(self.count()): - self.update_tab_title(idx) + self.update_tab_titles() def tabInserted(self, idx): """Update titles when a tab was inserted.""" diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 3b6d86c3d..72074e247 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -159,7 +159,7 @@ 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-only --force') quteproc.send_cmd(':tab-close --force') quteproc.wait_for_load_finished_url('about:blank') From 3e3f4b41643eeb509fbc56205d6f86a8e13d1c47 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 12 May 2017 11:06:17 -0700 Subject: [PATCH 45/56] Add :tab-only tests for --force --- tests/end2end/features/tabs.feature | 39 ++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index de1c02bcc..69fb41942 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1071,18 +1071,18 @@ Feature: Tab management - data/numbers/1.txt - data/numbers/2.txt - Scenario: :tab-pin prompt yes + 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 "Asking question *" in the log + 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) - Scenario: :tab-pin prompt no + 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 @@ -1094,6 +1094,39 @@ Feature: Tab management - data/numbers/1.txt - data/numbers/2.txt (active) + 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) + + 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) + - data/numbers/2.txt + + 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) + Scenario: :tab-pin open url When I open data/numbers/1.txt And I run :tab-pin From 2ae1bfc033f8fe4a127ce603be190d1ca071ba1d Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 12 May 2017 13:09:22 -0700 Subject: [PATCH 46/56] Keep pinned tabs in place rather than moving them. --- doc/help/commands.asciidoc | 3 ++- qutebrowser/browser/commands.py | 14 ++++---------- tests/end2end/features/sessions.feature | 7 ++----- tests/end2end/features/tabs.feature | 15 ++------------- 4 files changed, 10 insertions(+), 29 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index c79d83d08..05d1cd5a5 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -831,13 +831,14 @@ 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. ==== optional arguments * +*-p*+, +*--prev*+: Force selecting the tab before the current tab. * +*-n*+, +*--next*+: Force selecting the tab after the current tab. +* +*-f*+, +*--force*+: Avoid confirmation for pinned tabs. * +*-o*+, +*--opposite*+: Force selecting the tab in the opposite direction of what's configured in 'tabs->select-on-remove'. diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index af2da6869..2f7e6b6ab 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -257,28 +257,22 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window', name='tab-pin') - @cmdutils.argument('index') @cmdutils.argument('count', count=True) - def tab_pin(self, index=None, count=None): + def tab_pin(self, count=None): """Pin/Unpin the current tab. Args: - index: Location where the tab should be pinned/unpinned. count: The tab index to pin or unpin, or None """ - tabbar = self._tabbed_browser.tabBar() tab = self._cntwidget(count) if tab is None: return tab.data.pinned = not tab.data.pinned - if tab.data.pinned: - index = tabbar.pinned_count + 1 if index is None else int(index) - else: - index = self._count() if index is None else int(index) - self.tab_move(index) - self._tabbed_browser.set_tab_pinned(self._current_index(), + 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, tab.data.pinned) @cmdutils.register(instance='command-dispatcher', name='open', diff --git a/tests/end2end/features/sessions.feature b/tests/end2end/features/sessions.feature index d8c766632..64b97c80c 100644 --- a/tests/end2end/features/sessions.feature +++ b/tests/end2end/features/sessions.feature @@ -375,18 +375,15 @@ Feature: Saving and loading sessions 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-prev - And I run :tab-pin - And I run :tab-next + And I run :run-with-count 2 :tab-pin And I run :session-save pin_session And I run :tab-only And I run :tab-close --force And I run :session-load -c pin_session And I run :tab-prev And I run :open data/numbers/4.txt - And I wait 10s Then the message "Tab is pinned!" should be shown And the following tabs should be open: - - data/numbers/2.txt (active) - data/numbers/1.txt + - data/numbers/2.txt (active) - data/numbers/3.txt diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 69fb41942..93ac8f5cb 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1035,9 +1035,9 @@ Feature: Tab management 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/3.txt (active) - data/numbers/1.txt - data/numbers/2.txt + - data/numbers/3.txt (active) Scenario: :tab-pin unpin When I open data/numbers/1.txt @@ -1054,22 +1054,11 @@ Feature: Tab management 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 2 + And I run :run-with-count 2 :tab-pin Then the following tabs should be open: - data/numbers/1.txt - - data/numbers/3.txt (active) - data/numbers/2.txt - - Scenario: :tab-pin unpin to index 1 - 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 - And I run :tab-pin 1 - Then the following tabs should be open: - data/numbers/3.txt (active) - - data/numbers/1.txt - - data/numbers/2.txt Scenario: Pinned :tab-close prompt yes When I open data/numbers/1.txt From 1572be83be89f14a04226509bf1deaa4e06daf6c Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 12 May 2017 13:28:53 -0700 Subject: [PATCH 47/56] Add documentation for pinning --- doc/help/commands.asciidoc | 19 +++++++++++++++++-- doc/help/settings.asciidoc | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 05d1cd5a5..3c14a00f3 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -82,6 +82,7 @@ It is possible to run or bind multiple commands by separating them with `;;`. |<>|Move the current tab according to the argument and [count]. |<>|Switch to the next tab, or switch [count] tabs forward. |<>|Close all tabs except for the current one. +|<>|Pin the current/[count]th tab. |<>|Switch to the previous tab, or switch [count] tabs back. |<>|Unbind a keychain. |<>|Re-open a closed tab (optionally skipping [count] closed tabs). @@ -334,7 +335,7 @@ Syntax: +:help [*--tab*] [*--bg*] [*--window*] ['topic']+ Show help about a command or setting. ==== positional arguments -* +'topic'+: The topic to show help for. +* +'topic'+: The topic to show help for. - :__command__ for commands. - __section__\->__option__ for settings. @@ -893,13 +894,27 @@ 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 +Syntax: +:tab-pin+ + +Pin the current/[count]th tab. + +==== count +The tab index to pin + +==== note +* Pinning a tab shrinks it to `tabs->pinned-width` size. +* Attempting to close a pinned tab will cause a confirmation, unless `--force` is passed. [[tab-prev]] === tab-prev diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index b2479f959..ce9740278 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -124,6 +124,7 @@ |<>|Whether to wrap when changing tabs. |<>|Whether tabs should be movable. |<>|On which mouse button to close tabs. +|<>|The size of pinned tabs. |<>|The position of the tab bar. |<>|Whether to show favicons in the tab bar. |<>|Scale for favicons in the tab bar. The tab size is unchanged, so big favicons also require extra `tabs->padding`. @@ -131,6 +132,7 @@ |<>|Width of the progress indicator (0 to disable). |<>|Whether to open windows instead of tabs. |<>|The format to use for the tab title. The following placeholders are defined: +|<>|The format to use for the tab title, when it is pinned. The following placeholders are defined: |<>|Alignment of the text inside of tabs |<>|Switch between tabs using the mouse wheel. |<>|Padding for tabs (top, bottom, left, right). @@ -1183,6 +1185,14 @@ Valid values: Default: +pass:[middle]+ +[[tabs-pinned-width]] +=== pinned-width +The width for pinned tabs with a horizontal tabbar, in px. + +See `:tab-pin` for more details. + +Default: +43+ + [[tabs-position]] === position The position of the tab bar. @@ -1252,6 +1262,14 @@ 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 when it is pinned. + +The same syntax is used as `tabs->title-format` + +Default: +pass:[{index}]+ + [[tabs-title-alignment]] === title-alignment Alignment of the text inside of tabs From 068e47e22c21aaff92c26bde3cf5afe1414bc103 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 12 May 2017 16:56:07 -0700 Subject: [PATCH 48/56] Fix a few style issues --- qutebrowser/browser/commands.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 2f7e6b6ab..6e103d51f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -888,11 +888,11 @@ class CommandDispatcher: cur_idx = self._tabbed_browser.currentIndex() assert cur_idx != -1 - def _to_close(index): + 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)) + 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: @@ -900,7 +900,8 @@ class CommandDispatcher: 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)) + lambda: self.tab_only( + prev=prev, next_=next_, force=True)) return for i, tab in enumerate(self._tabbed_browser.widgets()): From a5eb3e27f8d89966b2e408a9459ec28032fdd495 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 12 May 2017 17:48:38 -0700 Subject: [PATCH 49/56] Fix some bugs in session saving test case --- tests/end2end/features/sessions.feature | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/end2end/features/sessions.feature b/tests/end2end/features/sessions.feature index 64b97c80c..bd1cb179e 100644 --- a/tests/end2end/features/sessions.feature +++ b/tests/end2end/features/sessions.feature @@ -377,11 +377,12 @@ Feature: Saving and loading sessions And I open data/numbers/3.txt in a new tab And I run :run-with-count 2 :tab-pin And I run :session-save pin_session - And I run :tab-only + And I run :tab-only --force And I run :tab-close --force And I run :session-load -c pin_session - And I run :tab-prev - And I run :open data/numbers/4.txt + 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 From b526c9a2a9ea79c0e7905618460c39b11d25008f Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 12 May 2017 18:38:06 -0700 Subject: [PATCH 50/56] Try to fix a pylint error --- qutebrowser/browser/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 6e103d51f..956925d71 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -853,7 +853,7 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) - def zoom(self, zoom: int=None, count=None): + def zoom(self, zoom: int = None, count=None): """Set the zoom level for the current tab. The zoom can be given as argument or as [count]. If neither is From 17169812bedf50bd6e111058e77399a33d941257 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sun, 14 May 2017 00:21:51 -0700 Subject: [PATCH 51/56] Misc cleanup and fixes --- qutebrowser/browser/browsertab.py | 2 +- qutebrowser/browser/commands.py | 2 +- qutebrowser/mainwindow/tabbedbrowser.py | 1 - qutebrowser/mainwindow/tabwidget.py | 17 ++++++----------- tests/end2end/features/sessions.feature | 4 ++-- tests/end2end/features/tabs.feature | 4 ++-- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 96e0cb838..f84ba4057 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -94,7 +94,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 + pinned: Flag to pin the tab. """ def __init__(self): diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 956925d71..dfcf33eaa 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -201,7 +201,7 @@ class CommandDispatcher: """Helper function for tab_close be able to handle message.async. Args: - tab: Tab select to be closed. + 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 diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 41a7d00df..02f555993 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -121,7 +121,6 @@ class TabbedBrowser(tabwidget.TabWidget): 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) - objreg.get('config').changed.connect(self.update_tab_titles_pinned) def __repr__(self): return utils.get_repr(self, count=self.count()) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 70f1b5088..6e2f92b9d 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -104,7 +104,6 @@ class TabWidget(QTabWidget): bar = self.tabBar() bar.set_tab_data(idx, 'pinned', pinned) self.update_tab_title(idx) - bar.update(bar.tabRect(idx)) if pinned: bar.pinned_count += 1 @@ -181,16 +180,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) - - @config.change_filter('tabs', 'title-format-pinned') - def update_tab_titles_pinned(self): - """Update all texts.""" - self.update_tab_titles() + 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.""" @@ -517,7 +512,7 @@ class TabBar(QTabBar): return size # If we *do* have enough space, tabs should occupy the whole window - # width. If there is pinned tabs their size will be substracted + # 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. diff --git a/tests/end2end/features/sessions.feature b/tests/end2end/features/sessions.feature index bd1cb179e..eb1d81e60 100644 --- a/tests/end2end/features/sessions.feature +++ b/tests/end2end/features/sessions.feature @@ -369,13 +369,13 @@ Feature: Saving and loading sessions Then the error "Session inexistent_session not found!" should be shown - # Test load/save of pinned tabs + # 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 :run-with-count 2 :tab-pin + 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 diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 93ac8f5cb..3816e7f1e 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1054,7 +1054,7 @@ Feature: Tab management 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 :run-with-count 2 :tab-pin + And I run :tab-pin with count 2 Then the following tabs should be open: - data/numbers/1.txt - data/numbers/2.txt @@ -1119,7 +1119,7 @@ Feature: Tab management Scenario: :tab-pin open url When I open data/numbers/1.txt And I run :tab-pin - And I run :open data/numbers/2.txt + 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) From 1142a19de9c40230fb09c6d3fb805c387f5093cd Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Tue, 16 May 2017 20:16:43 -0700 Subject: [PATCH 52/56] Add (pinned) keyword to 'following tabs open' tests --- tests/end2end/features/conftest.py | 32 ++++++++++++++++++------- tests/end2end/features/sessions.feature | 2 +- tests/end2end/features/tabs.feature | 23 +++++++++--------- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 72074e247..80d89bd35 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -535,31 +535,47 @@ 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 + + path = line + 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 (?Pprimary selection|clipboard) should ' r'contain "(?P.*)"')) diff --git a/tests/end2end/features/sessions.feature b/tests/end2end/features/sessions.feature index eb1d81e60..5ec6e168a 100644 --- a/tests/end2end/features/sessions.feature +++ b/tests/end2end/features/sessions.feature @@ -386,5 +386,5 @@ Feature: Saving and loading sessions 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) + - data/numbers/2.txt (active) (pinned) - data/numbers/3.txt diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 3816e7f1e..bec952d48 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1037,16 +1037,17 @@ Feature: Tab management Then the following tabs should be open: - data/numbers/1.txt - data/numbers/2.txt - - data/numbers/3.txt (active) + - 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 + - data/numbers/1.txt (pinned) - data/numbers/2.txt - data/numbers/3.txt (active) @@ -1057,7 +1058,7 @@ Feature: Tab management And I run :tab-pin with count 2 Then the following tabs should be open: - data/numbers/1.txt - - data/numbers/2.txt + - data/numbers/2.txt (pinned) - data/numbers/3.txt (active) Scenario: Pinned :tab-close prompt yes @@ -1069,7 +1070,7 @@ Feature: Tab management 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) + - data/numbers/1.txt (active) (pinned) Scenario: Pinned :tab-close prompt no When I open data/numbers/1.txt @@ -1080,8 +1081,8 @@ Feature: Tab management 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 - - data/numbers/2.txt (active) + - data/numbers/1.txt (pinned) + - data/numbers/2.txt (active) (pinned) Scenario: Pinned :tab-only prompt yes When I open data/numbers/1.txt @@ -1093,7 +1094,7 @@ Feature: Tab management 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) + - data/numbers/1.txt (active) (pinned) Scenario: Pinned :tab-only prompt no When I open data/numbers/1.txt @@ -1105,8 +1106,8 @@ Feature: Tab management 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) - - data/numbers/2.txt + - 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 @@ -1114,7 +1115,7 @@ Feature: Tab management And I run :tab-pin And I run :tab-only Then the following tabs should be open: - - data/numbers/2.txt (active) + - data/numbers/2.txt (active) (pinned) Scenario: :tab-pin open url When I open data/numbers/1.txt @@ -1122,4 +1123,4 @@ Feature: Tab management 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) + - data/numbers/1.txt (active) (pinned) From 5e3c68530a3452d0cccf4601d4b88a062d5c91f9 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Tue, 16 May 2017 21:03:33 -0700 Subject: [PATCH 53/56] Regenerate documentation --- README.asciidoc | 4 ++-- doc/help/commands.asciidoc | 16 +++++----------- doc/help/settings.asciidoc | 22 +++++++++------------- qutebrowser/browser/commands.py | 2 +- tests/end2end/features/conftest.py | 1 - 5 files changed, 17 insertions(+), 28 deletions(-) diff --git a/README.asciidoc b/README.asciidoc index 215e2e437..8e9af00b5 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -160,6 +160,7 @@ Contributors, sorted by the number of commits in descending order: * Lamar Pavel * Marshall Lochbaum * Bruno Oliveira +* thuck * Martin Tournoij * Imran Sobir * Alexander Cogneau @@ -174,6 +175,7 @@ Contributors, sorted by the number of commits in descending order: * Fritz Reichwald * Corentin Julé * meles5 +* Jay Kamat * Philipp Hansch * Panagiotis Ktistakis * Artur Shaik @@ -191,7 +193,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 @@ -225,7 +226,6 @@ Contributors, sorted by the number of commits in descending order: * Jussi Timperi * Cosmin Popescu * Brian Jackson -* thuck * sbinix * rsteube * neeasade diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 3c14a00f3..8e37acdac 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -82,7 +82,7 @@ It is possible to run or bind multiple commands by separating them with `;;`. |<>|Move the current tab according to the argument and [count]. |<>|Switch to the next tab, or switch [count] tabs forward. |<>|Close all tabs except for the current one. -|<>|Pin the current/[count]th tab. +|<>|Pin/Unpin the current/[count]th tab. |<>|Switch to the previous tab, or switch [count] tabs back. |<>|Unbind a keychain. |<>|Re-open a closed tab (optionally skipping [count] closed tabs). @@ -335,7 +335,7 @@ Syntax: +:help [*--tab*] [*--bg*] [*--window*] ['topic']+ Show help about a command or setting. ==== positional arguments -* +'topic'+: The topic to show help for. +* +'topic'+: The topic to show help for. - :__command__ for commands. - __section__\->__option__ for settings. @@ -839,9 +839,9 @@ Close the current/[count]th tab. ==== optional arguments * +*-p*+, +*--prev*+: Force selecting the tab before the current tab. * +*-n*+, +*--next*+: Force selecting the tab after the current tab. -* +*-f*+, +*--force*+: Avoid confirmation for pinned tabs. * +*-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 @@ -905,16 +905,10 @@ Close all tabs except for the current one. [[tab-pin]] === tab-pin -Syntax: +:tab-pin+ - -Pin the current/[count]th tab. +Pin/Unpin the current/[count]th tab. ==== count -The tab index to pin - -==== note -* Pinning a tab shrinks it to `tabs->pinned-width` size. -* Attempting to close a pinned tab will cause a confirmation, unless `--force` is passed. +The tab index to pin or unpin [[tab-prev]] === tab-prev diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index ce9740278..9beb2e9f4 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -124,15 +124,15 @@ |<>|Whether to wrap when changing tabs. |<>|Whether tabs should be movable. |<>|On which mouse button to close tabs. -|<>|The size of pinned tabs. |<>|The position of the tab bar. |<>|Whether to show favicons in the tab bar. |<>|Scale for favicons in the tab bar. The tab size is unchanged, so big favicons also require extra `tabs->padding`. |<>|The width of the tab bar if it's vertical, in px or as percentage of the window. +|<>|The width for pinned tabs with a horizontal tabbar, in px. |<>|Width of the progress indicator (0 to disable). |<>|Whether to open windows instead of tabs. |<>|The format to use for the tab title. The following placeholders are defined: -|<>|The format to use for the tab title, when it is pinned. The following placeholders are defined: +|<>|The format to use for the tab title for pinned tabs.The same placeholders like for title-format are defined. |<>|Alignment of the text inside of tabs |<>|Switch between tabs using the mouse wheel. |<>|Padding for tabs (top, bottom, left, right). @@ -1185,14 +1185,6 @@ Valid values: Default: +pass:[middle]+ -[[tabs-pinned-width]] -=== pinned-width -The width for pinned tabs with a horizontal tabbar, in px. - -See `:tab-pin` for more details. - -Default: +43+ - [[tabs-position]] === position The position of the tab bar. @@ -1229,6 +1221,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). @@ -1264,9 +1262,7 @@ Default: +pass:[{index}: {title}]+ [[tabs-title-format-pinned]] === title-format-pinned -The format to use for the tab title when it is pinned. - -The same syntax is used as `tabs->title-format` +The format to use for the tab title for pinned tabs.The same placeholders like for title-format are defined. Default: +pass:[{index}]+ diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index dfcf33eaa..f4591c9d6 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -259,7 +259,7 @@ class CommandDispatcher: name='tab-pin') @cmdutils.argument('count', count=True) def tab_pin(self, count=None): - """Pin/Unpin the current tab. + """Pin/Unpin the current/[count]th tab. Args: count: The tab index to pin or unpin, or None diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 80d89bd35..abcf744f7 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -549,7 +549,6 @@ def check_open_tabs(quteproc, request, tabs): assert line.startswith('- ') line = line[2:] # remove "- " prefix - path = line active = False pinned = False From 2a961c3951cc7c185e99540f78f95ad31cb2f7a7 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sun, 21 May 2017 19:49:10 -0700 Subject: [PATCH 54/56] Clean up pinned status to a centralized location - Add support for :tab-clone with pinned tabs Now tabbed_browser.set_tab_pinned can be called independently. --- qutebrowser/browser/commands.py | 8 ++++---- qutebrowser/mainwindow/tabwidget.py | 22 +++++++++++++++------- qutebrowser/misc/sessions.py | 2 +- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index f4591c9d6..532ead3d6 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -268,12 +268,10 @@ class CommandDispatcher: if tab is None: return - tab.data.pinned = not tab.data.pinned - + 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, - tab.data.pinned) + self._tabbed_browser.set_tab_pinned(tab_index, to_pin) @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') @@ -475,6 +473,7 @@ class CommandDispatcher: """ cmdutils.check_exclusive((bg, window), 'bw') curtab = self._current_widget() + pinned = curtab.data.pinned cur_title = self._tabbed_browser.page_title(self._current_index()) try: history = curtab.history.serialize() @@ -501,6 +500,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, pinned) return newtab @cmdutils.register(instance='command-dispatcher', scope='window') diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 6e2f92b9d..3e2b2c9c8 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -94,21 +94,29 @@ class TabWidget(QTabWidget): bar.set_tab_data(idx, 'indicator-color', color) bar.update(bar.tabRect(idx)) - def set_tab_pinned(self, idx, pinned): + 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() - bar.set_tab_data(idx, 'pinned', pinned) - self.update_tab_title(idx) + tab = self.widget(idx) - if pinned: - bar.pinned_count += 1 - else: - bar.pinned_count -= 1 + # 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() diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 94c5855bd..c33dc8045 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -396,7 +396,7 @@ class SessionManager(QObject): if tab.get('active', False): tab_to_focus = i if new_tab.data.pinned: - tabbed_browser.set_tab_pinned(i, 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): From 00f001729b29ed8f7b0f8210a3bab4778a873331 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sun, 21 May 2017 20:29:11 -0700 Subject: [PATCH 55/56] Fix undo with pinned tabs Add tests for undo with a pinned tab Add tests for clone with a pinned tab --- qutebrowser/mainwindow/tabbedbrowser.py | 9 ++++++--- qutebrowser/misc/sessions.py | 3 ++- tests/end2end/features/tabs.feature | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 02f555993..e1b9642fd 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -34,7 +34,8 @@ from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg, urlutils, message) -UndoEntry = collections.namedtuple('UndoEntry', ['url', 'history', 'index']) +UndoEntry = collections.namedtuple('UndoEntry', + ['url', 'history', 'index', 'pinned']) class TabDeletedError(Exception): @@ -294,7 +295,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 +327,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 +336,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): diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index c33dc8045..cd8d84c54 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -396,7 +396,8 @@ class SessionManager(QObject): 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) + 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): diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index bec952d48..00fe5c16a 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1124,3 +1124,23 @@ Feature: Tab management 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) From 419793c0b9ff4f293babea7623dcaf4787bbaa35 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sun, 21 May 2017 22:11:13 -0700 Subject: [PATCH 56/56] Misc cleanup and documentation update --- README.asciidoc | 2 +- doc/help/commands.asciidoc | 2 ++ doc/help/settings.asciidoc | 4 ++-- qutebrowser/browser/commands.py | 7 +++++-- qutebrowser/config/configdata.py | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.asciidoc b/README.asciidoc index 8e9af00b5..dab5352ed 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -171,11 +171,11 @@ Contributors, sorted by the number of commits in descending order: * Joel Torstensson * Patric Schmitz * Tarcisio Fedrizzi +* Jay Kamat * Claude * Fritz Reichwald * Corentin Julé * meles5 -* Jay Kamat * Philipp Hansch * Panagiotis Ktistakis * Artur Shaik diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 8e37acdac..31493485a 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -907,6 +907,8 @@ Close all tabs except for the current one. === 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 diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 9beb2e9f4..dd51acf26 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -132,7 +132,7 @@ |<>|Width of the progress indicator (0 to disable). |<>|Whether to open windows instead of tabs. |<>|The format to use for the tab title. The following placeholders are defined: -|<>|The format to use for the tab title for pinned tabs.The same placeholders like for title-format are defined. +|<>|The format to use for the tab title for pinned tabs. The same placeholders like for title-format are defined. |<>|Alignment of the text inside of tabs |<>|Switch between tabs using the mouse wheel. |<>|Padding for tabs (top, bottom, left, right). @@ -1262,7 +1262,7 @@ 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. +The format to use for the tab title for pinned tabs. The same placeholders like for title-format are defined. Default: +pass:[{index}]+ diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 532ead3d6..01a134422 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -261,6 +261,10 @@ class CommandDispatcher: 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 """ @@ -473,7 +477,6 @@ class CommandDispatcher: """ cmdutils.check_exclusive((bg, window), 'bw') curtab = self._current_widget() - pinned = curtab.data.pinned cur_title = self._tabbed_browser.page_title(self._current_index()) try: history = curtab.history.serialize() @@ -500,7 +503,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, pinned) + new_tabbed_browser.set_tab_pinned(idx, curtab.data.pinned) return newtab @cmdutils.register(instance='command-dispatcher', scope='window') diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 480e97a8d..c5d99cec6 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -727,7 +727,7 @@ def data(readonly=False): 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 format to use for the tab title for pinned tabs. " "The same placeholders like for title-format are defined."), ('title-alignment',