A new tab bar style

This commit is contained in:
Kovid Goyal 2018-06-06 13:27:15 +05:30
parent c76a8242e2
commit 53ae5a4f8d
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 104 additions and 18 deletions

View File

@ -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`)

View File

@ -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('#'):

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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):