Implement new minimal border style for tall layout

This commit is contained in:
Kovid Goyal
2020-08-30 13:49:34 +05:30
parent befae7e677
commit f324697c7c
3 changed files with 118 additions and 60 deletions

View File

@@ -7,7 +7,8 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
0.18.4 [2020-08-11] 0.18.4 [2020-08-11]
------------------- -------------------
- Grid/Splits layouts: Improve rendering of borders when using minimal borders - Improve rendering of borders when using minimal borders. Use less space and
does not display a box around active windows.
- Add support for displaying correct colors with non-sRGB PNG files (Adds a - Add support for displaying correct colors with non-sRGB PNG files (Adds a
dependency on liblcms2) dependency on liblcms2)

View File

@@ -5,14 +5,17 @@
from itertools import islice, repeat from itertools import islice, repeat
from typing import Dict, Generator, List, Optional, Sequence, Tuple from typing import Dict, Generator, List, Optional, Sequence, Tuple
from kitty.borders import BorderColor
from kitty.conf.utils import to_bool from kitty.conf.utils import to_bool
from kitty.constants import Edges
from kitty.typing import EdgeLiteral, WindowType from kitty.typing import EdgeLiteral, WindowType
from kitty.window_list import WindowList from kitty.window_list import WindowGroup, WindowList
from .base import ( from .base import (
Borders, Layout, LayoutDimension, LayoutOpts, NeighborsMap, all_borders, BorderLine, Layout, LayoutData, LayoutDimension, LayoutOpts, NeighborsMap,
lgd, no_borders, normalize_biases, safe_increment_bias, variable_bias lgd, normalize_biases, safe_increment_bias, variable_bias
) )
from .vertical import borders
def neighbors_for_tall_window( def neighbors_for_tall_window(
@@ -75,9 +78,7 @@ class Tall(Layout):
name = 'tall' name = 'tall'
main_is_horizontal = True main_is_horizontal = True
only_between_border = Borders(False, False, False, True) no_minimal_window_borders = True
only_main_border = Borders(False, False, True, False)
only_main_border_mirrored = Borders(True, False, False, False)
layout_opts = TallLayoutOpts({}) layout_opts = TallLayoutOpts({})
main_axis_layout = Layout.xlayout main_axis_layout = Layout.xlayout
perp_axis_layout = Layout.ylayout perp_axis_layout = Layout.ylayout
@@ -121,27 +122,27 @@ class Tall(Layout):
self.biased_map = candidate self.biased_map = candidate
return before != after return before != after
def do_layout(self, all_windows: WindowList) -> None: def simple_layout(self, all_windows: WindowList) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]:
num = all_windows.num_groups num = all_windows.num_groups
if num == 1:
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
return
is_fat = not self.main_is_horizontal is_fat = not self.main_is_horizontal
mirrored = self.layout_opts.mirrored mirrored = self.layout_opts.mirrored
groups = tuple(all_windows.iter_all_layoutable_groups()) groups = tuple(all_windows.iter_all_layoutable_groups())
main_bias = self.main_bias[::-1] if mirrored else self.main_bias main_bias = self.main_bias[::-1] if mirrored else self.main_bias
if num <= self.num_full_size_windows + 1: if mirrored:
if mirrored: groups = tuple(reversed(groups))
groups = tuple(reversed(groups)) main_bias = normalize_biases(main_bias[:num])
if num < self.num_full_size_windows + 1: xlayout = self.main_axis_layout(iter(groups), bias=main_bias)
main_bias = normalize_biases(main_bias[:num]) for wg, xl in zip(groups, xlayout):
xlayout = self.main_axis_layout(iter(groups), bias=main_bias) yl = next(self.perp_axis_layout(iter((wg,))))
for wg, xl in zip(groups, xlayout): if is_fat:
yl = next(self.perp_axis_layout(iter((wg,)))) xl, yl = yl, xl
if is_fat: yield wg, xl, yl, True
xl, yl = yl, xl
self.set_window_group_geometry(wg, xl, yl) def full_layout(self, all_windows: WindowList) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]:
return is_fat = not self.main_is_horizontal
mirrored = self.layout_opts.mirrored
groups = tuple(all_windows.iter_all_layoutable_groups())
main_bias = self.main_bias[::-1] if mirrored else self.main_bias
start = lgd.central.top if is_fat else lgd.central.left start = lgd.central.top if is_fat else lgd.central.left
size = 0 size = 0
@@ -156,19 +157,18 @@ class Tall(Layout):
yl = next(self.perp_axis_layout(iter((wg,)))) yl = next(self.perp_axis_layout(iter((wg,))))
if is_fat: if is_fat:
xl, yl = yl, xl xl, yl = yl, xl
self.set_window_group_geometry(wg, xl, yl) yield wg, xl, yl, True
else: else:
xlayout = self.main_axis_layout(islice(groups, self.num_full_size_windows + 1), bias=main_bias) xlayout = self.main_axis_layout(islice(groups, self.num_full_size_windows + 1), bias=main_bias)
attr: EdgeLiteral = 'bottom' if is_fat else 'right'
for i, wg in enumerate(groups): for i, wg in enumerate(groups):
if i >= self.num_full_size_windows: if i >= self.num_full_size_windows:
break break
xl = next(xlayout) xl = next(xlayout)
yl = next(self.perp_axis_layout(iter((wg,)))) yl = next(self.perp_axis_layout(iter((wg,))))
start = xl.content_pos + xl.content_size + xl.space_after
if is_fat: if is_fat:
xl, yl = yl, xl xl, yl = yl, xl
geom = self.set_window_group_geometry(wg, xl, yl) yield wg, xl, yl, True
start = getattr(geom, attr) + wg.decoration(attr)
size = (lgd.central.height if is_fat else lgd.central.width) - start size = (lgd.central.height if is_fat else lgd.central.width) - start
ylayout = self.variable_layout(all_windows, self.biased_map) ylayout = self.variable_layout(all_windows, self.biased_map)
@@ -179,36 +179,20 @@ class Tall(Layout):
xl = next(self.main_axis_layout(iter((wg,)), start=start, size=size)) xl = next(self.main_axis_layout(iter((wg,)), start=start, size=size))
if is_fat: if is_fat:
xl, yl = yl, xl xl, yl = yl, xl
yield wg, xl, yl, False
def do_layout(self, all_windows: WindowList) -> None:
num = all_windows.num_groups
if num == 1:
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
return
layouts = (self.simple_layout if num <= self.num_full_size_windows + 1 else self.full_layout)(all_windows)
for wg, xl, yl, is_full_size in layouts:
self.set_window_group_geometry(wg, xl, yl) self.set_window_group_geometry(wg, xl, yl)
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap: def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
return neighbors_for_tall_window(self.num_full_size_windows, window, windows, self.layout_opts.mirrored, self.main_is_horizontal) return neighbors_for_tall_window(self.num_full_size_windows, window, windows, self.layout_opts.mirrored, self.main_is_horizontal)
def minimal_borders(self, all_windows: WindowList, needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
mirrored = self.layout_opts.mirrored
only_main_border = self.only_main_border_mirrored if mirrored else self.only_main_border
num = all_windows.num_groups
last_i = num - 1
groups = tuple(all_windows.iter_all_layoutable_groups())
for i, wg in enumerate(groups):
if needs_borders_map[wg.id]:
yield all_borders
continue
if i < self.num_full_size_windows:
next_window_is_full_sized = last_i == i+1 or i+1 < self.num_full_size_windows
if next_window_is_full_sized and needs_borders_map[groups[i+1].id]:
yield no_borders
else:
yield no_borders if i == last_i else only_main_border
continue
if i == last_i:
yield no_borders
break
if needs_borders_map[groups[i+1].id]:
yield no_borders
else:
yield self.only_between_border
def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> Optional[bool]: def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> Optional[bool]:
if action_name == 'increase_num_full_size_windows': if action_name == 'increase_num_full_size_windows':
self.layout_opts.full_size += 1 self.layout_opts.full_size += 1
@@ -218,13 +202,83 @@ class Tall(Layout):
self.layout_opts.full_size -= 1 self.layout_opts.full_size -= 1
return True return True
def window_independent_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
num = all_windows.num_groups
if num == 1 or not lgd.draw_minimal_borders:
return
bw = next(all_windows.iter_all_layoutable_groups()).effective_border()
if not bw:
return
if num <= self.num_full_size_windows + 1:
layout = (x[:3] for x in self.simple_layout(all_windows))
yield from borders(layout, self.main_is_horizontal, all_windows)
return
main_layouts: List[Tuple[WindowGroup, LayoutData, LayoutData]] = []
perp_borders: List[BorderLine] = []
layouts = (self.simple_layout if num <= self.num_full_size_windows else self.full_layout)(all_windows)
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
active_group = all_windows.active_group
mirrored = self.layout_opts.mirrored
for wg, xl, yl, is_full_size in layouts:
if is_full_size:
main_layouts.append((wg, xl, yl))
else:
color = BorderColor.inactive
if needs_borders_map.get(wg.id):
color = BorderColor.active if wg is active_group else BorderColor.bell
if self.main_is_horizontal:
e1 = Edges(
xl.content_pos - xl.space_before,
yl.content_pos - yl.space_before,
xl.content_pos + xl.content_size + xl.space_after,
yl.content_pos - yl.space_before + bw
)
e3 = Edges(
xl.content_pos - xl.space_before,
yl.content_pos + yl.content_size + yl.space_after - bw,
xl.content_pos + xl.content_size + xl.space_after,
yl.content_pos + yl.content_size + yl.space_after,
)
e2 = Edges(
xl.content_pos + ((xl.content_size + xl.space_after - bw) if mirrored else -xl.space_before),
yl.content_pos - yl.space_before,
xl.content_pos + ((xl.content_size + xl.space_after) if mirrored else (bw - xl.space_before)),
yl.content_pos + yl.content_size + yl.space_after,
)
else:
e1 = Edges(
xl.content_pos - xl.space_before,
yl.content_pos - yl.space_before,
xl.content_pos - xl.space_before + bw,
yl.content_pos + yl.content_size + yl.space_after,
)
e3 = Edges(
xl.content_pos + xl.content_size + xl.space_after - bw,
yl.content_pos - yl.space_before,
xl.content_pos + xl.content_size + xl.space_after,
yl.content_pos + yl.content_size + yl.space_after,
)
e2 = Edges(
xl.content_pos - xl.space_before,
yl.content_pos + ((yl.content_size + yl.space_after - bw) if mirrored else -yl.space_before),
xl.content_pos + xl.content_size + xl.space_after,
yl.content_pos + ((yl.content_size + yl.space_after) if mirrored else (bw - yl.space_before)),
)
perp_borders.append(BorderLine(e1, color))
perp_borders.append(BorderLine(e2, color))
perp_borders.append(BorderLine(e3, color))
mirrored = self.layout_opts.mirrored
yield from borders(
main_layouts, self.main_is_horizontal, all_windows,
start_offset=int(not mirrored), end_offset=int(mirrored)
)
yield from perp_borders[1:-1]
class Fat(Tall): class Fat(Tall):
name = 'fat' name = 'fat'
main_is_horizontal = False main_is_horizontal = False
only_between_border = Borders(False, False, True, False)
only_main_border = Borders(False, False, False, True)
only_main_border_mirrored = Borders(False, True, False, False)
main_axis_layout = Layout.ylayout main_axis_layout = Layout.ylayout
perp_axis_layout = Layout.xlayout perp_axis_layout = Layout.xlayout

View File

@@ -18,13 +18,13 @@ from .base import (
def borders( def borders(
data: Iterable[Tuple[WindowGroup, LayoutData, LayoutData]], data: Iterable[Tuple[WindowGroup, LayoutData, LayoutData]],
is_horizontal: bool, is_horizontal: bool,
all_windows: WindowList all_windows: WindowList,
start_offset: int = 1, end_offset: int = 1
) -> Generator[BorderLine, None, None]: ) -> Generator[BorderLine, None, None]:
borders: List[BorderLine] = [] borders: List[BorderLine] = []
active_group = all_windows.active_group active_group = all_windows.active_group
groups = tuple(all_windows.iter_all_layoutable_groups())
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders) needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
bw = groups[0].effective_border() bw = next(all_windows.iter_all_layoutable_groups()).effective_border()
if not bw: if not bw:
return return
@@ -52,8 +52,11 @@ def borders(
color = BorderColor.active if wg is active_group else BorderColor.bell color = BorderColor.active if wg is active_group else BorderColor.bell
borders.append(BorderLine(e1, color)) borders.append(BorderLine(e1, color))
borders.append(BorderLine(e2, color)) borders.append(BorderLine(e2, color))
for x in borders[1:-1]:
yield x last_idx = len(borders) - 1 - end_offset
for i, x in enumerate(borders):
if start_offset <= i <= last_idx:
yield x
class Vertical(Layout): class Vertical(Layout):