Tab bar: Improve empty space management when some tabs have short titles, allocate the saved space to the active tab

Fixes #5548
This commit is contained in:
Kovid Goyal 2022-10-03 13:24:21 +05:30
parent a116e3cadd
commit 647b18d345
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 50 additions and 11 deletions

View File

@ -44,6 +44,8 @@ Detailed list of changes
- X11: Fix a regression in the previous release that caused pasting from GTK based applications to have extra newlines (:iss:`5528`)
- Tab bar: Improve empty space management when some tabs have short titles, allocate the saved space to the active tab (:iss:`5548`)
0.26.3 [2022-09-22]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -5,7 +5,7 @@ import os
from functools import lru_cache, partial, wraps
from string import Formatter as StringFormatter
from typing import (
Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union
Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union,
)
from .borders import Border, BorderColor
@ -13,7 +13,7 @@ from .config import build_ansi_color_table
from .constants import config_dir
from .fast_data_types import (
DECAWM, Color, Region, Screen, cell_size_for_window, get_boss, get_options,
pt_to_px, set_tab_bar_render_data, viewport_for_window
pt_to_px, set_tab_bar_render_data, viewport_for_window,
)
from .rgb import alpha_blend, color_as_sgr, color_from_int, to_color
from .types import WindowGeometry, run_once
@ -151,6 +151,7 @@ class SupSub:
class ExtraData:
prev_tab: Optional[TabBarData] = None
next_tab: Optional[TabBarData] = None
for_layout: bool = False
def draw_attributed_string(title: str, screen: Screen) -> None:
@ -435,6 +436,7 @@ class TabBar:
self.num_tabs = 1
self.data_buffer_size = 0
self.blank_rects: Tuple[Border, ...] = ()
self.cell_ranges: List[Tuple[int, int]] = []
self.laid_out_once = False
self.apply_options()
@ -571,31 +573,66 @@ class TabBar:
if not self.laid_out_once:
return
s = self.screen
s.cursor.x = 0
s.erase_in_line(2, False)
max_title_length = max(1, (self.screen.columns // max(1, len(data))) - 1)
cr = []
last_tab = data[-1] if data else None
ed = ExtraData()
for i, t in enumerate(data):
def draw_tab(i: int, tab: TabBarData, cell_ranges: List[Tuple[int, int]], max_tab_length: int, check_overflow: bool = True) -> None:
ed.prev_tab = data[i - 1] if i > 0 else None
ed.next_tab = data[i + 1] if i + 1 < len(data) else None
s.cursor.bg = as_rgb(self.draw_data.tab_bg(t))
s.cursor.fg = as_rgb(self.draw_data.tab_fg(t))
s.cursor.bold, s.cursor.italic = self.active_font_style if t.is_active else self.inactive_font_style
before = s.cursor.x
end = self.draw_func(self.draw_data, s, t, before, max_title_length, i + 1, t is last_tab, ed)
end = self.draw_func(self.draw_data, s, t, before, max_tab_length, i + 1, t is last_tab, ed)
s.cursor.bg = s.cursor.fg = 0
cr.append((before, end))
if s.cursor.x > s.columns - max_title_length and t is not last_tab:
cell_ranges.append((before, end))
if check_overflow and s.cursor.x > s.columns - max_tab_length and t is not last_tab:
s.cursor.x = s.columns - 2
s.cursor.bg = as_rgb(color_as_int(self.draw_data.default_bg))
s.cursor.fg = as_rgb(0xff0000)
s.draw('')
raise StopIteration()
unconstrained_tab_length = max(1, s.columns - 2)
ideal_tab_lengths = [i for i in range(len(data))]
default_max_tab_length = max(1, (s.columns // max(1, len(data))) - 1)
max_tab_lengths = [default_max_tab_length for _ in range(len(data))]
active_idx = 0
extra = 0
ed.for_layout = True
for i, t in enumerate(data):
s.cursor.x = 0
draw_tab(i, t, [], unconstrained_tab_length, check_overflow=False)
ideal_tab_lengths[i] = tl = max(1, s.cursor.x)
if t.is_active:
active_idx = i
if tl < default_max_tab_length:
max_tab_lengths[i] = tl
extra += default_max_tab_length - tl
if extra > 0:
if ideal_tab_lengths[active_idx] > max_tab_lengths[active_idx]:
d = min(extra, ideal_tab_lengths[active_idx] - max_tab_lengths[active_idx])
max_tab_lengths[active_idx] += d
extra -= d
if extra > 0:
over_achievers = tuple(i for i in range(len(data)) if ideal_tab_lengths[i] > max_tab_lengths[i])
if over_achievers:
amt_per_over_achiever = extra // len(over_achievers)
if amt_per_over_achiever > 0:
for i in over_achievers:
max_tab_lengths[i] += amt_per_over_achiever
s.cursor.x = 0
s.erase_in_line(2, False)
cr: List[Tuple[int, int]] = []
ed.for_layout = False
for i, t in enumerate(data):
try:
draw_tab(i, t, cr, max_tab_lengths[i])
except StopIteration:
break
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
self.cell_ranges = cr
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
self.align()
def align_with_factor(self, factor: int = 1) -> None: