Use similar border drawing techniques for splits

This commit is contained in:
Kovid Goyal 2020-08-30 08:37:16 +05:30
parent c99f7c554f
commit cf3f9d68b3
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 120 additions and 38 deletions

View File

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

View File

@ -211,7 +211,7 @@ class Layout:
name: Optional[str] = None
needs_window_borders = True
must_draw_borders = False # can be overridden to customize behavior from kittens
no_minimal_window_borders = False
no_minimal_window_borders = False # if True means minimal borders are drawn in window_independent_borders
layout_opts = LayoutOpts({})
only_active_window_visible = False
@ -337,10 +337,11 @@ def xlayout(
bias: Optional[Sequence[float]] = None,
start: Optional[int] = None,
size: Optional[int] = None,
offset: int = 0
offset: int = 0,
border_mult: int = 1
) -> LayoutDimension:
decoration_pairs = tuple(
(g.decoration('left'), g.decoration('right')) for i, g in
(g.decoration('left', border_mult=border_mult), g.decoration('right', border_mult=border_mult)) for i, g in
enumerate(groups) if i >= offset
)
if start is None:
@ -355,10 +356,11 @@ def ylayout(
bias: Optional[Sequence[float]] = None,
start: Optional[int] = None,
size: Optional[int] = None,
offset: int = 0
offset: int = 0,
border_mult: int = 1
) -> LayoutDimension:
decoration_pairs = tuple(
(g.decoration('top'), g.decoration('bottom')) for i, g in
(g.decoration('top', border_mult=border_mult), g.decoration('bottom', border_mult=border_mult)) for i, g in
enumerate(groups) if i >= offset
)
if start is None:

View File

@ -3,19 +3,26 @@
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from typing import (
Collection, Dict, Generator, Optional, Sequence, Tuple, Union
Collection, Dict, Generator, List, NamedTuple, Optional, Sequence, Tuple,
Union
)
from kitty.borders import BorderColor
from kitty.constants import Edges, WindowGeometry
from kitty.typing import EdgeLiteral, WindowType
from kitty.window_list import WindowGroup, WindowList
from .base import (
Layout, LayoutOpts, NeighborsMap, blank_rects_for_window, lgd,
window_geometry_from_layouts, BorderLine
BorderLine, Layout, LayoutOpts, NeighborsMap, blank_rects_for_window, lgd,
window_geometry_from_layouts
)
class Extent(NamedTuple):
start: int = 0
end: int = 0
class Pair:
def __init__(self, horizontal: bool = True):
@ -23,11 +30,13 @@ def __init__(self, horizontal: bool = True):
self.one: Optional[Union[Pair, int]] = None
self.two: Optional[Union[Pair, int]] = None
self.bias = 0.5
self.between_border: Optional[Edges] = None
self.top = self.left = self.width = self.height = 0
self.between_borders: List[Edges] = []
self.first_extent = self.second_extent = Extent()
def __repr__(self) -> str:
return 'Pair(horizontal={}, bias={:.2f}, one={}, two={}, between_border={})'.format(
self.horizontal, self.bias, self.one, self.two, self.between_border)
return 'Pair(horizontal={}, bias={:.2f}, one={}, two={}, between_borders={})'.format(
self.horizontal, self.bias, self.one, self.two, self.between_borders)
def all_window_ids(self) -> Generator[int, None, None]:
if self.one is not None:
@ -168,7 +177,12 @@ def layout_pair(
id_window_map: Dict[int, WindowGroup],
layout_object: Layout
) -> None:
self.between_border = None
self.between_borders = []
self.left, self.top, self.width, self.height = left, top, width, height
bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0
border_mult = 0 if lgd.draw_minimal_borders else 1
bw2 = bw * 2
self.first_extent = self.second_extent = Extent()
if self.one is None or self.two is None:
q = self.one or self.two
if isinstance(q, Pair):
@ -176,56 +190,62 @@ def layout_pair(
if q is None:
return
wg = id_window_map[q]
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width))
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height))
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width, border_mult=border_mult))
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height, border_mult=border_mult))
geom = window_geometry_from_layouts(xl, yl)
self.first_extent = Extent(left, left + width)
self.apply_window_geometry(q, geom, id_window_map, layout_object)
return
bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0
b1 = bw // 2
b2 = bw - b1
if self.horizontal:
w1 = max(2*lgd.cell_width + 1, int(self.bias * width) - b1)
w2 = max(2*lgd.cell_width + 1, width - w1 - b1 - b2)
w1 = max(2*lgd.cell_width + 1, int(self.bias * width) - bw)
w2 = max(2*lgd.cell_width + 1, width - w1 - bw2)
self.first_extent = Extent(max(0, left - bw), left + w1 + bw)
self.second_extent = Extent(left + w1 + bw, left + width + bw)
if isinstance(self.one, Pair):
self.one.layout_pair(left, top, w1, height, id_window_map, layout_object)
else:
wg = id_window_map[self.one]
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height))
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=w1))
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height, border_mult=border_mult))
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=w1, border_mult=border_mult))
geom = window_geometry_from_layouts(xl, yl)
self.apply_window_geometry(self.one, geom, id_window_map, layout_object)
if b1 + b2:
self.between_border = Edges(left + w1, top, left + w1 + b1 + b2, top + height)
left += b1 + b2
self.between_borders = [
Edges(left + w1, top, left + w1 + bw, top + height),
Edges(left + w1 + bw, top, left + w1 + bw2, top + height),
]
left += bw2
if isinstance(self.two, Pair):
self.two.layout_pair(left + w1, top, w2, height, id_window_map, layout_object)
else:
wg = id_window_map[self.two]
xl = next(layout_object.xlayout(iter((wg,)), start=left + w1, size=w2))
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height))
xl = next(layout_object.xlayout(iter((wg,)), start=left + w1, size=w2, border_mult=border_mult))
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height, border_mult=border_mult))
geom = window_geometry_from_layouts(xl, yl)
self.apply_window_geometry(self.two, geom, id_window_map, layout_object)
else:
h1 = max(2*lgd.cell_height + 1, int(self.bias * height) - b1)
h2 = max(2*lgd.cell_height + 1, height - h1 - b1 - b2)
h1 = max(2*lgd.cell_height + 1, int(self.bias * height) - bw)
h2 = max(2*lgd.cell_height + 1, height - h1 - bw2)
self.first_extent = Extent(max(0, top - bw), top + h1 + bw)
self.second_extent = Extent(top + h1 + bw, top + height + bw)
if isinstance(self.one, Pair):
self.one.layout_pair(left, top, width, h1, id_window_map, layout_object)
else:
wg = id_window_map[self.one]
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width))
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=h1))
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width, border_mult=border_mult))
yl = next(layout_object.ylayout(iter((wg,)), start=top, size=h1, border_mult=border_mult))
geom = window_geometry_from_layouts(xl, yl)
self.apply_window_geometry(self.one, geom, id_window_map, layout_object)
if b1 + b2:
self.between_border = Edges(left, top + h1, left + width, top + h1 + b1 + b2)
top += b1 + b2
self.between_borders = [
Edges(left, top + h1, left + width, top + h1 + bw),
Edges(left, top + h1 + bw, left + width, top + h1 + bw2),
]
top += bw2
if isinstance(self.two, Pair):
self.two.layout_pair(left, top + h1, width, h2, id_window_map, layout_object)
else:
wg = id_window_map[self.two]
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width))
yl = next(layout_object.ylayout(iter((wg,)), start=top + h1, size=h2))
xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width, border_mult=border_mult))
yl = next(layout_object.ylayout(iter((wg,)), start=top + h1, size=h2, border_mult=border_mult))
geom = window_geometry_from_layouts(xl, yl)
self.apply_window_geometry(self.two, geom, id_window_map, layout_object)
@ -244,6 +264,55 @@ def modify_size_of_child(self, which: int, increment: float, is_horizontal: bool
return parent.modify_size_of_child(which, increment, is_horizontal, layout_object)
return False
def borders_for_window(self, layout_object: 'Splits', window_id: int) -> Generator[Edges, None, None]:
is_first = self.one == window_id
if self.between_borders:
yield self.between_borders[0 if is_first else 1]
q = self
found_same_direction = found_transverse1 = found_transverse2 = False
while not (found_same_direction and found_transverse1 and found_transverse2):
parent = q.parent(layout_object.pairs_root)
if parent is None:
break
q = parent
if not q.between_borders:
continue
if q.horizontal == self.horizontal:
if not found_same_direction:
if self.horizontal:
is_before = q.between_borders[0].left <= self.left
else:
is_before = q.between_borders[0].top <= self.top
if is_before == is_first:
found_same_direction = True
edges = q.between_borders[1 if is_before else 0]
if self.horizontal:
yield edges._replace(top=self.top, bottom=self.top + self.height)
else:
yield edges._replace(left=self.left, right=self.left + self.width)
else:
if self.horizontal:
is_before = q.between_borders[0].top <= self.top
else:
is_before = q.between_borders[0].left <= self.left
extent = self.first_extent if is_first else self.second_extent
if is_before:
if not found_transverse1:
found_transverse1 = True
edges = q.between_borders[1]
if self.horizontal:
yield edges._replace(left=extent.start, right=extent.end)
else:
yield edges._replace(top=extent.start, bottom=extent.end)
else:
if not found_transverse2:
found_transverse2 = True
edges = q.between_borders[0]
if self.horizontal:
yield edges._replace(left=extent.start, right=extent.end)
else:
yield edges._replace(top=extent.start, bottom=extent.end)
def neighbors_for_window(self, window_id: int, ans: NeighborsMap, layout_object: 'Splits', all_windows: WindowList) -> None:
def quadrant(is_horizontal: bool, is_first: bool) -> Tuple[EdgeLiteral, EdgeLiteral]:
@ -325,6 +394,7 @@ class Splits(Layout):
name = 'splits'
needs_all_windows = True
layout_opts = SplitsLayoutOpts({})
no_minimal_window_borders = True
@property
def default_axis_is_horizontal(self) -> bool:
@ -423,8 +493,18 @@ def window_independent_borders(self, all_windows: WindowList) -> Generator[Borde
if not lgd.draw_minimal_borders or window_count < 2:
return
for pair in self.pairs_root.self_and_descendants():
if pair.between_border is not None:
yield BorderLine(pair.between_border)
for edges in pair.between_borders:
yield BorderLine(edges)
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
ag = all_windows.active_group
active_group_id = -1 if ag is None else ag.id
for grp_id, needs_borders in needs_borders_map.items():
if needs_borders:
qpair = self.pairs_root.pair_for_window(grp_id)
if qpair is not None:
color = BorderColor.active if grp_id is active_group_id else BorderColor.bell
for edges in qpair.borders_for_window(self, grp_id):
yield BorderLine(edges, color)
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
wg = all_windows.group_for_window(window)