Implement new minimal border style for tall layout
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user