diff --git a/docs/changelog.rst b/docs/changelog.rst index ca521c74e..06def6f38 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,6 +7,8 @@ To update |kitty|, :doc:`follow the instructions `. 0.18.4 [2020-08-11] ------------------- +- Grid layout: Improve rendering of borders when using minimal borders + - Add support for displaying correct colors with non-sRGB PNG files (Adds a dependency on liblcms2) diff --git a/kitty/borders.py b/kitty/borders.py index a07c61322..b27d774ef 100644 --- a/kitty/borders.py +++ b/kitty/borders.py @@ -111,6 +111,6 @@ class Borders: colors = window_bg, window_bg, window_bg, window_bg draw_edges(self.os_window_id, self.tab_id, colors, wg) - color = BorderColor.inactive - for (left, top, right, bottom) in current_layout.window_independent_borders(all_windows): - add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, color) + for border_line in current_layout.window_independent_borders(all_windows): + left, top, right, bottom = border_line.edges + add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, border_line.color) diff --git a/kitty/layout/base.py b/kitty/layout/base.py index e7ef83f6e..0596676e4 100644 --- a/kitty/layout/base.py +++ b/kitty/layout/base.py @@ -16,6 +16,7 @@ from kitty.fast_data_types import ( from kitty.options_stub import Options from kitty.typing import TypedDict, WindowType from kitty.window_list import WindowGroup, WindowList +from kitty.borders import BorderColor class Borders(NamedTuple): @@ -25,6 +26,11 @@ class Borders(NamedTuple): bottom: bool +class BorderLine(NamedTuple): + edges: Edges = Edges() + color: BorderColor = BorderColor.inactive + + class LayoutOpts: def __init__(self, data: Dict[str, str]): @@ -205,6 +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 layout_opts = LayoutOpts({}) only_active_window_visible = False @@ -383,13 +390,14 @@ class Layout: for i in range(all_windows.num_groups): yield all_borders - def window_independent_borders(self, windows: WindowList) -> Generator[Edges, None, None]: + def window_independent_borders(self, windows: WindowList) -> Generator[BorderLine, None, None]: return - yield Edges() # type: ignore + yield BorderLine() # type: ignore def minimal_borders(self, windows: WindowList, needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]: - for needs_border in needs_borders_map.values(): - yield all_borders if needs_border else no_borders + if not self.no_minimal_window_borders: + for needs_border in needs_borders_map.values(): + yield all_borders if needs_border else no_borders def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> Optional[bool]: pass diff --git a/kitty/layout/grid.py b/kitty/layout/grid.py index 4897bfdee..7c64d0466 100644 --- a/kitty/layout/grid.py +++ b/kitty/layout/grid.py @@ -5,15 +5,18 @@ from functools import lru_cache from itertools import repeat from math import ceil, floor -from typing import Callable, Dict, Generator, List, Optional, Sequence, Tuple +from typing import ( + Callable, Dict, Generator, List, Optional, Sequence, Set, Tuple +) +from kitty.borders import BorderColor from kitty.constants import Edges from kitty.typing import WindowType from kitty.window_list import WindowGroup, WindowList from .base import ( - Layout, LayoutData, LayoutDimension, ListOfWindows, NeighborsMap, - layout_dimension, lgd, variable_bias + BorderLine, Layout, LayoutData, LayoutDimension, ListOfWindows, + NeighborsMap, layout_dimension, lgd, variable_bias ) from .tall import neighbors_for_tall_window @@ -35,6 +38,7 @@ def calc_grid_size(n: int) -> Tuple[int, int, int, int]: class Grid(Layout): name = 'grid' + no_minimal_window_borders = True def remove_all_biases(self) -> bool: self.biased_rows: Dict[int, float] = {} @@ -165,9 +169,7 @@ class Grid(Layout): def position_window_in_grid_cell(window_idx: int, xl: LayoutData, yl: LayoutData) -> None: wg = groups[window_idx] - edges = Edges( - wg.decoration('left'), wg.decoration('top'), wg.decoration('right'), wg.decoration('bottom') - ) + edges = Edges(wg.decoration('left'), wg.decoration('top'), wg.decoration('right'), wg.decoration('bottom')) xl = layout(xl, lgd.cell_width, edges.left, edges.right) yl = layout(yl, lgd.cell_height, edges.top, edges.bottom) self.set_window_group_geometry(wg, xl, yl) @@ -176,34 +178,72 @@ class Grid(Layout): n, nrows, ncols, special_rows, special_col, on_col_done): position_window_in_grid_cell(window_idx, xl, yl) - def window_independent_borders(self, all_windows: WindowList) -> Generator[Edges, None, None]: + def window_independent_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]: n = all_windows.num_groups if not lgd.draw_minimal_borders or n < 2: return + needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders) ncols, nrows, special_rows, special_col = calc_grid_size(n) - row_borders: List[List[Edges]] = [[]] - col_borders: List[Edges] = [] + is_first_row: Set[int] = set() + is_last_row: Set[int] = set() + is_first_column: Set[int] = set() + is_last_column: Set[int] = set() groups = tuple(all_windows.iter_all_layoutable_groups()) bw = groups[0].effective_border() + if not bw: + return xl: LayoutData = LayoutData() yl: LayoutData = LayoutData() + prev_col_windows: List[int] = [] + layout_data_map: Dict[int, Tuple[LayoutData, LayoutData]] = {} def on_col_done(col_windows: List[int]) -> None: - left = xl.content_pos + xl.content_size + xl.space_after - bw // 2 - col_borders.append(Edges(left, lgd.central.top, left + bw, lgd.central.bottom)) - row_borders.append([]) + nonlocal prev_col_windows, is_first_column + if col_windows: + is_first_row.add(groups[col_windows[0]].id) + is_last_row.add(groups[col_windows[-1]].id) + if not prev_col_windows: + is_first_column = {groups[x].id for x in col_windows} + prev_col_windows = col_windows + all_groups_in_order = [] for window_idx, xl, yl in self.layout_windows(n, nrows, ncols, special_rows, special_col, on_col_done): - top = yl.content_pos + yl.content_size + yl.space_after - bw // 2 - right = xl.content_pos + xl.content_size + xl.space_after - row_borders[-1].append(Edges(xl.content_pos - xl.space_before, top, right, top + bw)) + wg = groups[window_idx] + all_groups_in_order.append(wg) + layout_data_map[wg.id] = xl, yl + is_last_column = {groups[x].id for x in prev_col_windows} + active_group = all_windows.active_group - for border in col_borders[:-1]: - yield border + def ends(yl: LayoutData) -> Tuple[int, int]: + return yl.content_pos - yl.space_before, yl.content_pos + yl.content_size + yl.space_after - for rows in row_borders: - for border in rows[:-1]: - yield border + def borders_for_window(gid: int) -> Generator[Edges, None, None]: + xl, yl = layout_data_map[gid] + left, right = ends(xl) + top, bottom = ends(yl) + first_row, last_row = gid in is_first_row, gid in is_last_row + first_column, last_column = gid in is_first_column, gid in is_last_column + + # Horizontal + if not first_row: + yield Edges(left, top, right, top + bw) + if not last_row: + yield Edges(left, bottom - bw, right, bottom) + + # Vertical + if not first_column: + yield Edges(left, top, left + bw, bottom) + if not last_column: + yield Edges(right - bw, top, right, bottom) + + for wg in all_groups_in_order: + for edges in borders_for_window(wg.id): + yield BorderLine(edges) + for wg in all_groups_in_order: + if needs_borders_map.get(wg.id): + color = BorderColor.active if wg is active_group else BorderColor.bell + for edges in borders_for_window(wg.id): + yield BorderLine(edges, color) def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap: n = all_windows.num_groups diff --git a/kitty/layout/splits.py b/kitty/layout/splits.py index 293853882..e1506ee8c 100644 --- a/kitty/layout/splits.py +++ b/kitty/layout/splits.py @@ -12,7 +12,7 @@ from kitty.window_list import WindowGroup, WindowList from .base import ( Layout, LayoutOpts, NeighborsMap, blank_rects_for_window, lgd, - window_geometry_from_layouts + window_geometry_from_layouts, BorderLine ) @@ -417,14 +417,14 @@ class Splits(Layout): pair.bias = 0.5 return True - def window_independent_borders(self, all_windows: WindowList) -> Generator[Edges, None, None]: + def window_independent_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]: groups = tuple(all_windows.iter_all_layoutable_groups()) window_count = len(groups) 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 pair.between_border + yield BorderLine(pair.between_border) def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap: wg = all_windows.group_for_window(window)