A new tab bar style
This commit is contained in:
parent
c76a8242e2
commit
53ae5a4f8d
@ -6,18 +6,23 @@ Changelog
|
||||
0.11.0 [future]
|
||||
------------------------------
|
||||
|
||||
- A new tab bar style "fade" in which each tabs edges fade into the background.
|
||||
See :opt:`tab_bar_style` and :opt:`tab_fade` for details. The old look can be
|
||||
restored by setting tab_bar_style to "separator".
|
||||
|
||||
- :doc:`Pre-compiled binaries <binary>` with all bundled dependencies for Linux
|
||||
(:iss:`595`)
|
||||
|
||||
- A :doc:`new kitten <kittens/panel>` to create dock panels on X11 desktops showing the output from
|
||||
arbitrary terminal programs.
|
||||
- A :doc:`new kitten <kittens/panel>` to create dock panels on X11 desktops
|
||||
showing the output from arbitrary terminal programs.
|
||||
|
||||
- Reduce data sent to the GPU per render by 30% (:commit:`8dea5b3`)
|
||||
|
||||
- Implement changing the font size for individual top level (OS) windows
|
||||
(:iss:`408`)
|
||||
|
||||
- ssh kitten: Support all SSH options. It can now be aliased directly to ssh for convenience. (:pull:`591`)
|
||||
- ssh kitten: Support all SSH options. It can now be aliased directly to ssh
|
||||
for convenience. (:pull:`591`)
|
||||
|
||||
- icat kitten: Add :option:`kitty +kitten icat --print-window-size` to easily
|
||||
detect the window size in pixels from scripting languages (:iss:`581`)
|
||||
|
||||
@ -43,6 +43,18 @@ def python_string(text):
|
||||
return ast.literal_eval("'''" + text.replace("'''", "'\\''") + "'''")
|
||||
|
||||
|
||||
def choices(*choices):
|
||||
defval = choices[0]
|
||||
choices = frozenset(choices)
|
||||
|
||||
def choice(x):
|
||||
x = x.lower()
|
||||
if x not in choices:
|
||||
x = defval
|
||||
return x
|
||||
return choice
|
||||
|
||||
|
||||
def parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes):
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
|
||||
@ -8,14 +8,13 @@ from gettext import gettext as _
|
||||
from . import fast_data_types as defines
|
||||
from .conf.definition import option_func
|
||||
from .conf.utils import (
|
||||
positive_float, positive_int, to_cmdline, to_color, unit_float
|
||||
choices, positive_float, positive_int, to_cmdline, to_color, unit_float
|
||||
)
|
||||
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||
from .layout import all_layouts
|
||||
from .rgb import color_as_int, color_as_sharp, color_from_int
|
||||
from .utils import log_error
|
||||
|
||||
|
||||
MINIMUM_FONT_SIZE = 4
|
||||
|
||||
|
||||
@ -490,8 +489,27 @@ Which edge to show the tab bar on, top or bottom'''))
|
||||
o('tab_bar_margin_width', 0.0, option_type=positive_float, long_text=_('''
|
||||
The margin to the left and right of the tab bar (in pts)'''))
|
||||
|
||||
o('tab_bar_style', 'fade', option_type=choices('fade', 'separator'), long_text=_('''
|
||||
The tab bar style, can be one of: :code:`fade` or :code:`separator`. In the fade style,
|
||||
each tab's edges fade into the background color, in the separator style, tabs are
|
||||
separated by a configurable separator.
|
||||
'''))
|
||||
|
||||
|
||||
def tab_fade(x):
|
||||
return tuple(map(unit_float, x.split()))
|
||||
|
||||
|
||||
o('tab_fade', '0.25 0.5 0.75 1', option_type=tab_fade, long_text=_('''
|
||||
Control how each tab fades into the background when using :code:`fade` for the
|
||||
:opt:`tab_bar_style`. Each number is an alpha (between zero and one) that controls
|
||||
how much the corresponding cell fades into the background, with zero being no fade
|
||||
and one being full fade. You can change the number of cells used by adding/removing
|
||||
entries to this list.
|
||||
'''))
|
||||
|
||||
o('tab_separator', '"{}"'.format(default_tab_separator), option_type=tab_separator, long_text=_('''
|
||||
The separator between tabs in the tab bar'''))
|
||||
The separator between tabs in the tab bar when using :code:`separator` as the :opt:`tab_bar_style`.'''))
|
||||
|
||||
o('active_tab_foreground', '#000', option_type=to_color, long_text=_('''
|
||||
Tab bar colors and styles'''))
|
||||
|
||||
12
kitty/rgb.py
generated
12
kitty/rgb.py
generated
@ -8,6 +8,18 @@ from collections import namedtuple
|
||||
Color = namedtuple('Color', 'red green blue')
|
||||
|
||||
|
||||
def alpha_blend_channel(top_color, bottom_color, alpha):
|
||||
return int(alpha * top_color + (1 - alpha) * bottom_color)
|
||||
|
||||
|
||||
def alpha_blend(top_color, bottom_color, alpha):
|
||||
return Color(
|
||||
alpha_blend_channel(top_color.red, bottom_color.red, alpha),
|
||||
alpha_blend_channel(top_color.green, bottom_color.green, alpha),
|
||||
alpha_blend_channel(top_color.blue, bottom_color.blue, alpha)
|
||||
)
|
||||
|
||||
|
||||
def parse_single_color(c):
|
||||
if len(c) == 1:
|
||||
c += c
|
||||
|
||||
@ -13,20 +13,29 @@ from .fast_data_types import (
|
||||
from .layout import Rect
|
||||
from .utils import color_as_int
|
||||
from .window import calculate_gl_geometry
|
||||
from .rgb import alpha_blend
|
||||
|
||||
TabBarData = namedtuple('TabBarData', 'title is_active is_last needs_attention')
|
||||
DrawData = namedtuple('DrawData', 'leading_spaces sep trailing_spaces bell_on_tab bell_fg')
|
||||
TabBarData = namedtuple('TabBarData', 'title is_active needs_attention')
|
||||
DrawData = namedtuple('DrawData', 'leading_spaces sep trailing_spaces bell_on_tab bell_fg alpha active_bg inactive_bg default_bg')
|
||||
|
||||
|
||||
def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length):
|
||||
if draw_data.leading_spaces:
|
||||
screen.draw(' ' * draw_data.leading_spaces)
|
||||
def as_rgb(x):
|
||||
return (x << 8) | 2
|
||||
|
||||
|
||||
def draw_title(draw_data, screen, tab):
|
||||
if tab.needs_attention and draw_data.bell_on_tab:
|
||||
fg = screen.cursor.fg
|
||||
screen.cursor.fg = draw_data.bell_fg
|
||||
screen.draw('🔔 ')
|
||||
screen.cursor.fg = fg
|
||||
screen.draw(tab.title)
|
||||
|
||||
|
||||
def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length):
|
||||
if draw_data.leading_spaces:
|
||||
screen.draw(' ' * draw_data.leading_spaces)
|
||||
draw_title(draw_data, screen, tab)
|
||||
if draw_data.trailing_spaces:
|
||||
screen.draw(' ' * draw_data.trailing_spaces)
|
||||
extra = screen.cursor.x - before - max_title_length
|
||||
@ -40,6 +49,33 @@ def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length):
|
||||
return end
|
||||
|
||||
|
||||
def draw_tab_with_fade(draw_data, screen, tab, before, max_title_length):
|
||||
tab_bg = draw_data.active_bg if tab.is_active else draw_data.inactive_bg
|
||||
fade_colors = [as_rgb(color_as_int(alpha_blend(tab_bg, draw_data.default_bg, alpha))) for alpha in draw_data.alpha]
|
||||
for bg in fade_colors:
|
||||
screen.cursor.bg = bg
|
||||
screen.draw(' ')
|
||||
draw_title(draw_data, screen, tab)
|
||||
extra = screen.cursor.x - before - max_title_length
|
||||
if extra > 0:
|
||||
screen.cursor.x = before
|
||||
draw_title(draw_data, screen, tab)
|
||||
extra = screen.cursor.x - before - max_title_length
|
||||
if extra > 0:
|
||||
screen.cursor.x -= extra + 1
|
||||
screen.draw('…')
|
||||
for bg in reversed(fade_colors):
|
||||
if extra >= 0:
|
||||
break
|
||||
extra += 1
|
||||
screen.cursor.bg = bg
|
||||
screen.draw(' ')
|
||||
end = screen.cursor.x
|
||||
screen.cursor.bg = as_rgb(color_as_int(draw_data.default_bg))
|
||||
screen.draw(' ')
|
||||
return end
|
||||
|
||||
|
||||
class TabBar:
|
||||
|
||||
def __init__(self, os_window_id, opts):
|
||||
@ -70,13 +106,15 @@ class TabBar:
|
||||
self.active_font_style = opts.active_tab_font_style
|
||||
self.inactive_font_style = opts.inactive_tab_font_style
|
||||
|
||||
def as_rgb(x):
|
||||
return (x << 8) | 2
|
||||
|
||||
self.active_bg = as_rgb(color_as_int(opts.active_tab_background))
|
||||
self.active_fg = as_rgb(color_as_int(opts.active_tab_foreground))
|
||||
self.bell_fg = as_rgb(0xff0000)
|
||||
self.draw_data = DrawData(self.leading_spaces, self.sep, self.trailing_spaces, self.opts.bell_on_tab, self.bell_fg)
|
||||
self.draw_data = DrawData(
|
||||
self.leading_spaces, self.sep, self.trailing_spaces, self.opts.bell_on_tab, self.bell_fg,
|
||||
self.opts.tab_fade, self.opts.active_tab_background, self.opts.inactive_tab_background,
|
||||
self.opts.background
|
||||
)
|
||||
self.draw_func = draw_tab_with_separator if self.opts.tab_bar_style == 'separator' else draw_tab_with_fade
|
||||
|
||||
def patch_colors(self, spec):
|
||||
if 'active_tab_foreground' in spec:
|
||||
@ -117,15 +155,16 @@ class TabBar:
|
||||
s.erase_in_line(2, False)
|
||||
max_title_length = (self.screen_geometry.xnum // max(1, len(data))) - 1
|
||||
cr = []
|
||||
last_tab = data[-1] if data else None
|
||||
|
||||
for t in data:
|
||||
s.cursor.bg = self.active_bg if t.is_active else 0
|
||||
s.cursor.fg = self.active_fg if t.is_active else 0
|
||||
s.cursor.bold, s.cursor.italic = self.active_font_style if t.is_active else self.inactive_font_style
|
||||
before = s.cursor.x
|
||||
end = draw_tab_with_separator(self, s, t, before, max_title_length)
|
||||
end = self.draw_func(self.draw_data, s, t, before, max_title_length)
|
||||
cr.append((before, end))
|
||||
if s.cursor.x > s.columns - max_title_length and not t.is_last:
|
||||
if s.cursor.x > s.columns - max_title_length and t is not last_tab:
|
||||
s.draw('…')
|
||||
break
|
||||
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
|
||||
|
||||
@ -473,7 +473,7 @@ class TabManager: # {{{
|
||||
if w.needs_attention:
|
||||
needs_attention = True
|
||||
break
|
||||
ans.append(TabBarData(title, t is at, t is self.tabs[-1], needs_attention))
|
||||
ans.append(TabBarData(title, t is at, needs_attention))
|
||||
return ans
|
||||
|
||||
def activate_tab_at(self, x):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user