Tall and Fat layouts ported to new groups API
This commit is contained in:
parent
e844ad6db3
commit
01c0e8da93
@ -4,15 +4,16 @@
|
||||
|
||||
from enum import IntFlag
|
||||
from itertools import chain
|
||||
from typing import List, Optional, Sequence, Tuple
|
||||
from typing import Sequence, Tuple
|
||||
|
||||
from .fast_data_types import (
|
||||
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
|
||||
os_window_has_background_image
|
||||
)
|
||||
from .options_stub import Options
|
||||
from .typing import LayoutType
|
||||
from .utils import load_shaders
|
||||
from .typing import WindowType, LayoutType
|
||||
from .window_list import WindowGroup, WindowList
|
||||
|
||||
|
||||
class BorderColor(IntFlag):
|
||||
@ -30,10 +31,12 @@ def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, lef
|
||||
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
|
||||
|
||||
|
||||
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], window: WindowType, borders: bool = False) -> None:
|
||||
geometry = window.geometry
|
||||
pl, pt = window.effective_padding('left'), window.effective_padding('top')
|
||||
pr, pb = window.effective_padding('right'), window.effective_padding('bottom')
|
||||
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], wg: WindowGroup, borders: bool = False) -> None:
|
||||
geometry = wg.geometry
|
||||
if geometry is None:
|
||||
return
|
||||
pl, pt = wg.effective_padding('left'), wg.effective_padding('top')
|
||||
pr, pb = wg.effective_padding('right'), wg.effective_padding('bottom')
|
||||
left = geometry.left - pl
|
||||
top = geometry.top - pt
|
||||
lr = geometry.right
|
||||
@ -41,7 +44,7 @@ def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], window: Wi
|
||||
bt = geometry.bottom
|
||||
bottom = bt + pb
|
||||
if borders:
|
||||
width = window.effective_border()
|
||||
width = wg.effective_border()
|
||||
bt = bottom
|
||||
lr = right
|
||||
left -= width
|
||||
@ -69,8 +72,7 @@ class Borders:
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
windows: List[WindowType],
|
||||
active_window: Optional[WindowType],
|
||||
all_windows: WindowList,
|
||||
current_layout: LayoutType,
|
||||
extra_blank_rects: Sequence[Tuple[int, int, int, int]],
|
||||
draw_window_borders: bool = True,
|
||||
@ -82,31 +84,33 @@ class Borders:
|
||||
left, top, right, bottom = br
|
||||
add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, BorderColor.default_bg)
|
||||
bw = 0
|
||||
if windows:
|
||||
bw = windows[0].effective_border()
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))
|
||||
if groups:
|
||||
bw = groups[0].effective_border()
|
||||
draw_borders = bw > 0 and draw_window_borders
|
||||
if draw_borders:
|
||||
border_data = current_layout.resolve_borders(windows, active_window)
|
||||
border_data = current_layout.resolve_borders(all_windows)
|
||||
active_group = all_windows.active_group
|
||||
|
||||
for i, w in enumerate(windows):
|
||||
window_bg = w.screen.color_profile.default_bg
|
||||
for i, wg in enumerate(groups):
|
||||
window_bg = wg.default_bg
|
||||
window_bg = (window_bg << 8) | BorderColor.window_bg
|
||||
if draw_borders:
|
||||
# Draw the border rectangles
|
||||
if w is active_window and self.draw_active_borders:
|
||||
if wg is active_group and self.draw_active_borders:
|
||||
color = BorderColor.active
|
||||
else:
|
||||
color = BorderColor.bell if w.needs_attention else BorderColor.inactive
|
||||
color = BorderColor.bell if wg.needs_attention else BorderColor.inactive
|
||||
try:
|
||||
colors = tuple(color if needed else window_bg for needed in next(border_data))
|
||||
draw_edges(self.os_window_id, self.tab_id, colors, w, borders=True)
|
||||
draw_edges(self.os_window_id, self.tab_id, colors, wg, borders=True)
|
||||
except StopIteration:
|
||||
pass
|
||||
if not has_background_image:
|
||||
# Draw the background rectangles over the padding region
|
||||
colors = window_bg, window_bg, window_bg, window_bg
|
||||
draw_edges(self.os_window_id, self.tab_id, colors, w)
|
||||
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(windows, active_window):
|
||||
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)
|
||||
|
||||
@ -4,10 +4,9 @@
|
||||
|
||||
from functools import partial
|
||||
from itertools import repeat
|
||||
from operator import attrgetter
|
||||
from typing import (
|
||||
Dict, Generator, Iterable, List, NamedTuple, Optional, Sequence, Tuple,
|
||||
Union, cast
|
||||
Dict, Generator, Iterable, Iterator, List, NamedTuple, Optional, Sequence,
|
||||
Tuple, Union, cast
|
||||
)
|
||||
|
||||
from kitty.constants import Edges, WindowGeometry
|
||||
@ -47,20 +46,13 @@ LayoutDimension = Generator[LayoutData, None, None]
|
||||
ListOfWindows = List[WindowType]
|
||||
|
||||
|
||||
class InternalNeighborsMap(TypedDict):
|
||||
class NeighborsMap(TypedDict):
|
||||
left: List[int]
|
||||
top: List[int]
|
||||
right: List[int]
|
||||
bottom: List[int]
|
||||
|
||||
|
||||
class NeighborsMap(TypedDict):
|
||||
left: Tuple[int, ...]
|
||||
top: Tuple[int, ...]
|
||||
right: Tuple[int, ...]
|
||||
bottom: Tuple[int, ...]
|
||||
|
||||
|
||||
class LayoutGlobalData:
|
||||
draw_minimal_borders: bool = True
|
||||
draw_active_borders: bool = True
|
||||
@ -241,7 +233,10 @@ class Layout:
|
||||
return False
|
||||
|
||||
def modify_size_of_window(self, all_windows: WindowList, window_id: int, increment: float, is_horizontal: bool = True) -> bool:
|
||||
return self.apply_bias(window_id, increment, all_windows, is_horizontal)
|
||||
idx = all_windows.group_idx_for_window(window_id)
|
||||
if idx is None:
|
||||
return False
|
||||
return self.apply_bias(idx, increment, all_windows, is_horizontal)
|
||||
|
||||
def parse_layout_opts(self, layout_opts: Optional[str] = None) -> LayoutOpts:
|
||||
data: Dict[str, str] = {}
|
||||
@ -264,20 +259,12 @@ class Layout:
|
||||
def neighbors(self, all_windows: WindowList) -> NeighborsMap:
|
||||
w = all_windows.active_window
|
||||
assert w is not None
|
||||
n = self.neighbors_for_window(w, all_windows)
|
||||
id_getter = attrgetter('id')
|
||||
ans: NeighborsMap = {
|
||||
'left': tuple(map(id_getter, n['left'])),
|
||||
'top': tuple(map(id_getter, n['top'])),
|
||||
'right': tuple(map(id_getter, n['right'])),
|
||||
'bottom': tuple(map(id_getter, n['bottom']))
|
||||
}
|
||||
return ans
|
||||
return self.neighbors_for_window(w, all_windows)
|
||||
|
||||
def move_window(self, all_windows: WindowList, delta: Union[str, int] = 1) -> bool:
|
||||
# delta can be either a number or a string such as 'left', 'top', etc
|
||||
# for neighborhood moves
|
||||
if len(all_windows) < 2 or not delta:
|
||||
if all_windows.num_groups < 2 or not delta:
|
||||
return False
|
||||
|
||||
if isinstance(delta, int):
|
||||
@ -291,7 +278,7 @@ class Layout:
|
||||
q: List[int] = cast(List[int], neighbors.get(which, []))
|
||||
if not q:
|
||||
return False
|
||||
return all_windows.move_window_group(to_group_with_window_id=q[0])
|
||||
return all_windows.move_window_group(to_group=q[0])
|
||||
|
||||
def add_window(self, all_windows: WindowList, window: WindowType, location: Optional[str] = None, overlay_for: Optional[int] = None) -> None:
|
||||
if overlay_for is not None and overlay_for in all_windows:
|
||||
@ -349,7 +336,7 @@ class Layout:
|
||||
|
||||
def xlayout(
|
||||
self,
|
||||
all_windows: WindowList,
|
||||
groups: Iterator[WindowGroup],
|
||||
bias: Optional[Sequence[float]] = None,
|
||||
start: Optional[int] = None,
|
||||
size: Optional[int] = None,
|
||||
@ -357,7 +344,7 @@ class Layout:
|
||||
) -> LayoutDimension:
|
||||
decoration_pairs = tuple(
|
||||
(g.decoration('left'), g.decoration('right')) for i, g in
|
||||
enumerate(all_windows.iter_all_layoutable_groups()) if i >= offset
|
||||
enumerate(groups) if i >= offset
|
||||
)
|
||||
if start is None:
|
||||
start = lgd.central.left
|
||||
@ -367,7 +354,7 @@ class Layout:
|
||||
|
||||
def ylayout(
|
||||
self,
|
||||
all_windows: WindowList,
|
||||
groups: Iterator[WindowGroup],
|
||||
bias: Optional[Sequence[float]] = None,
|
||||
start: Optional[int] = None,
|
||||
size: Optional[int] = None,
|
||||
@ -375,7 +362,7 @@ class Layout:
|
||||
) -> LayoutDimension:
|
||||
decoration_pairs = tuple(
|
||||
(g.decoration('top'), g.decoration('bottom')) for i, g in
|
||||
enumerate(all_windows.iter_all_layoutable_groups()) if i >= offset
|
||||
enumerate(groups) if i >= offset
|
||||
)
|
||||
if start is None:
|
||||
start = lgd.central.top
|
||||
@ -383,37 +370,35 @@ class Layout:
|
||||
size = lgd.central.height
|
||||
return layout_dimension(start, size, lgd.cell_height, decoration_pairs, bias=bias, left_align=lgd.align_top_left)
|
||||
|
||||
def set_window_geometry(self, w: WindowType, idx: int, xl: LayoutData, yl: LayoutData) -> None:
|
||||
wg = window_geometry_from_layouts(xl, yl)
|
||||
w.set_geometry(wg)
|
||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
||||
def set_window_group_geometry(self, wg: WindowGroup, xl: LayoutData, yl: LayoutData) -> WindowGeometry:
|
||||
geom = window_geometry_from_layouts(xl, yl)
|
||||
wg.set_geometry(geom)
|
||||
self.blank_rects.extend(blank_rects_for_window(geom))
|
||||
return geom
|
||||
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
return {'left': [], 'right': [], 'top': [], 'bottom': []}
|
||||
|
||||
def compute_needs_borders_map(self, windows: WindowList, active_window: Optional[WindowType]) -> Dict[int, bool]:
|
||||
return {w.id: ((w is active_window and lgd.draw_active_borders) or w.needs_attention) for w in windows}
|
||||
def compute_needs_borders_map(self, all_windows: WindowList) -> Dict[int, bool]:
|
||||
return all_windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||
|
||||
def resolve_borders(self, windows: WindowList, active_window: Optional[WindowType]) -> Generator[Borders, None, None]:
|
||||
def resolve_borders(self, windows: WindowList) -> Generator[Borders, None, None]:
|
||||
if lgd.draw_minimal_borders:
|
||||
needs_borders_map = self.compute_needs_borders_map(windows, active_window)
|
||||
yield from self.minimal_borders(windows, active_window, needs_borders_map)
|
||||
needs_borders_map = self.compute_needs_borders_map(windows)
|
||||
yield from self.minimal_borders(windows, needs_borders_map)
|
||||
else:
|
||||
yield from Layout.minimal_borders(self, windows, active_window, {})
|
||||
yield from Layout.minimal_borders(self, windows, {})
|
||||
|
||||
def window_independent_borders(self, windows: WindowList, active_window: Optional[WindowType] = None) -> Generator[Edges, None, None]:
|
||||
return
|
||||
yield Edges() # type: ignore
|
||||
|
||||
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
|
||||
for w in windows:
|
||||
if w is not active_window or lgd.draw_active_borders or w.needs_attention:
|
||||
yield all_borders
|
||||
else:
|
||||
yield no_borders
|
||||
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
|
||||
|
||||
def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@ -11,7 +11,7 @@ from kitty.typing import WindowType
|
||||
from kitty.window_list import WindowList
|
||||
|
||||
from .base import (
|
||||
Borders, InternalNeighborsMap, Layout, LayoutData, LayoutDimension,
|
||||
Borders, NeighborsMap, Layout, LayoutData, LayoutDimension,
|
||||
ListOfWindows, all_borders, layout_dimension, lgd, no_borders,
|
||||
variable_bias
|
||||
)
|
||||
@ -213,7 +213,7 @@ class Grid(Layout):
|
||||
for border in rows[:-1]:
|
||||
yield border
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
n = len(windows)
|
||||
if n < 4:
|
||||
return neighbors_for_tall_window(1, window, windows)
|
||||
|
||||
@ -11,7 +11,7 @@ from kitty.typing import EdgeLiteral, WindowType
|
||||
from kitty.window_list import WindowList
|
||||
|
||||
from .base import (
|
||||
Borders, InternalNeighborsMap, Layout, LayoutOpts, all_borders,
|
||||
Borders, NeighborsMap, Layout, LayoutOpts, all_borders,
|
||||
blank_rects_for_window, lgd, no_borders, window_geometry_from_layouts
|
||||
)
|
||||
|
||||
@ -234,7 +234,7 @@ class Pair:
|
||||
return parent.modify_size_of_child(which, increment, is_horizontal, layout_object)
|
||||
return False
|
||||
|
||||
def neighbors_for_window(self, window_id: int, ans: InternalNeighborsMap, layout_object: 'Splits') -> None:
|
||||
def neighbors_for_window(self, window_id: int, ans: NeighborsMap, layout_object: 'Splits') -> None:
|
||||
|
||||
def quadrant(is_horizontal: bool, is_first: bool) -> Tuple[EdgeLiteral, EdgeLiteral]:
|
||||
if is_horizontal:
|
||||
@ -407,10 +407,10 @@ class Splits(Layout):
|
||||
if pair.between_border is not None:
|
||||
yield pair.between_border
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
window_id = window.overlay_for or window.id
|
||||
pair = self.pairs_root.pair_for_window(window_id)
|
||||
ans: InternalNeighborsMap = {'left': [], 'right': [], 'top': [], 'bottom': []}
|
||||
ans: NeighborsMap = {'left': [], 'right': [], 'top': [], 'bottom': []}
|
||||
if pair is not None:
|
||||
pair.neighbors_for_window(window_id, ans, self)
|
||||
return ans
|
||||
|
||||
@ -3,30 +3,32 @@
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from itertools import repeat
|
||||
from typing import Dict, Generator, List, Optional, Tuple
|
||||
from typing import Dict, Generator, List, Tuple
|
||||
|
||||
from kitty.typing import EdgeLiteral, WindowType
|
||||
from kitty.window_list import WindowList
|
||||
|
||||
from .base import (
|
||||
Borders, InternalNeighborsMap, Layout, LayoutDimension, LayoutOpts,
|
||||
ListOfWindows, all_borders, lgd, no_borders, normalize_biases,
|
||||
safe_increment_bias, variable_bias
|
||||
Borders, Layout, LayoutDimension, LayoutOpts, NeighborsMap, all_borders,
|
||||
lgd, no_borders, normalize_biases, safe_increment_bias, variable_bias
|
||||
)
|
||||
|
||||
|
||||
def neighbors_for_tall_window(num_full_size_windows: int, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
idx = windows.index(window)
|
||||
prev = None if idx == 0 else windows[idx-1]
|
||||
nxt = None if idx == len(windows) - 1 else windows[idx+1]
|
||||
ans: InternalNeighborsMap = {'left': [prev.id] if prev is not None else [], 'right': [], 'top': [], 'bottom': []}
|
||||
def neighbors_for_tall_window(num_full_size_windows: int, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
||||
wg = all_windows.group_for_window(window)
|
||||
assert wg is not None
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
idx = groups.index(wg)
|
||||
prev = None if idx == 0 else groups[idx-1]
|
||||
nxt = None if idx == len(groups) - 1 else groups[idx+1]
|
||||
ans: NeighborsMap = {'left': [prev.id] if prev is not None else [], 'right': [], 'top': [], 'bottom': []}
|
||||
if idx < num_full_size_windows - 1:
|
||||
if nxt is not None:
|
||||
ans['right'] = [nxt.id]
|
||||
elif idx == num_full_size_windows - 1:
|
||||
ans['right'] = [w.id for w in windows[idx+1:]]
|
||||
ans['right'] = [w.id for w in groups[idx+1:]]
|
||||
else:
|
||||
ans['left'] = [windows[num_full_size_windows - 1].id]
|
||||
ans['left'] = [groups[num_full_size_windows - 1].id]
|
||||
if idx > num_full_size_windows and prev is not None:
|
||||
ans['top'] = [prev.id]
|
||||
if nxt is not None:
|
||||
@ -72,13 +74,13 @@ class Tall(Layout):
|
||||
self.biased_map: Dict[int, float] = {}
|
||||
return True
|
||||
|
||||
def variable_layout(self, windows: ListOfWindows, biased_map: Dict[int, float]) -> LayoutDimension:
|
||||
windows = windows[self.num_full_size_windows:]
|
||||
bias = variable_bias(len(windows), biased_map) if len(windows) > 1 else None
|
||||
return self.perp_axis_layout(windows, bias=bias)
|
||||
def variable_layout(self, all_windows: WindowList, biased_map: Dict[int, float]) -> LayoutDimension:
|
||||
num = all_windows.num_groups - self.num_full_size_windows
|
||||
bias = variable_bias(num, biased_map) if num > 1 else None
|
||||
return self.perp_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias, offset=self.num_full_size_windows)
|
||||
|
||||
def apply_bias(self, idx: int, increment: float, top_level_windows: ListOfWindows, is_horizontal: bool = True) -> bool:
|
||||
num_windows = len(top_level_windows)
|
||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
num_windows = all_windows.num_groups
|
||||
if self.main_is_horizontal == is_horizontal:
|
||||
before_main_bias = self.main_bias
|
||||
ncols = self.num_full_size_windows + 1
|
||||
@ -93,63 +95,67 @@ class Tall(Layout):
|
||||
if idx < self.num_full_size_windows or num_of_short_windows < 2:
|
||||
return False
|
||||
idx -= self.num_full_size_windows
|
||||
before_layout = list(self.variable_layout(top_level_windows, self.biased_map))
|
||||
before_layout = list(self.variable_layout(all_windows, self.biased_map))
|
||||
before = self.biased_map.get(idx, 0.)
|
||||
candidate = self.biased_map.copy()
|
||||
candidate[idx] = after = before + increment
|
||||
if before_layout == list(self.variable_layout(top_level_windows, candidate)):
|
||||
if before_layout == list(self.variable_layout(all_windows, candidate)):
|
||||
return False
|
||||
self.biased_map = candidate
|
||||
return before != after
|
||||
|
||||
def do_layout(self, windows: WindowList, active_window_idx: int) -> None:
|
||||
if len(windows) == 1:
|
||||
self.layout_single_window(windows[0])
|
||||
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
|
||||
is_fat = not self.main_is_horizontal
|
||||
if len(windows) <= self.num_full_size_windows + 1:
|
||||
xlayout = self.main_axis_layout(windows, bias=self.main_bias)
|
||||
for i, (w, xl) in enumerate(zip(windows, xlayout)):
|
||||
yl = next(self.perp_axis_layout([w]))
|
||||
if num <= self.num_full_size_windows + 1:
|
||||
xlayout = self.main_axis_layout(all_windows.iter_all_layoutable_groups(), bias=self.main_bias)
|
||||
for i, (wg, xl) in enumerate(zip(all_windows.iter_all_layoutable_groups(), xlayout)):
|
||||
yl = next(self.perp_axis_layout(iter((wg,))))
|
||||
if is_fat:
|
||||
xl, yl = yl, xl
|
||||
self.set_window_geometry(w, i, xl, yl)
|
||||
self.set_window_group_geometry(wg, xl, yl)
|
||||
return
|
||||
|
||||
xlayout = self.main_axis_layout(windows[:self.num_full_size_windows + 1], bias=self.main_bias)
|
||||
main_axis_groups = (gr for i, gr in enumerate(all_windows.iter_all_layoutable_groups()) if i <= self.num_full_size_windows)
|
||||
xlayout = self.main_axis_layout(main_axis_groups, bias=self.main_bias)
|
||||
attr: EdgeLiteral = 'bottom' if is_fat else 'right'
|
||||
start = lgd.central.top if is_fat else lgd.central.left
|
||||
for i, w in enumerate(windows):
|
||||
for i, wg in enumerate(all_windows.iter_all_layoutable_groups()):
|
||||
if i >= self.num_full_size_windows:
|
||||
break
|
||||
xl = next(xlayout)
|
||||
yl = next(self.perp_axis_layout([w]))
|
||||
yl = next(self.perp_axis_layout(iter((wg,))))
|
||||
if is_fat:
|
||||
xl, yl = yl, xl
|
||||
self.set_window_geometry(w, i, xl, yl)
|
||||
start = getattr(w.geometry, attr) + w.effective_border() + w.effective_margin(attr) + w.effective_padding(attr)
|
||||
ylayout = self.variable_layout(windows, self.biased_map)
|
||||
geom = self.set_window_group_geometry(wg, xl, yl)
|
||||
start = getattr(geom, attr) + wg.decoration(attr)
|
||||
ylayout = self.variable_layout(all_windows, self.biased_map)
|
||||
size = (lgd.central.height if is_fat else lgd.central.width) - start
|
||||
for i, w in enumerate(windows):
|
||||
for i, wg in enumerate(all_windows.iter_all_layoutable_groups()):
|
||||
if i < self.num_full_size_windows:
|
||||
continue
|
||||
yl = next(ylayout)
|
||||
xl = next(self.main_axis_layout([w], start=start, size=size))
|
||||
xl = next(self.main_axis_layout(iter((wg,)), start=start, size=size))
|
||||
if is_fat:
|
||||
xl, yl = yl, xl
|
||||
self.set_window_geometry(w, i, xl, yl)
|
||||
self.set_window_group_geometry(wg, xl, yl)
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
return neighbors_for_tall_window(self.num_full_size_windows, window, windows)
|
||||
|
||||
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
|
||||
last_i = len(windows) - 1
|
||||
for i, w in enumerate(windows):
|
||||
if needs_borders_map[w.id]:
|
||||
def minimal_borders(self, all_windows: WindowList, needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
|
||||
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:
|
||||
if (last_i == i+1 or i+1 < self.num_full_size_windows) and needs_borders_map[windows[i+1].id]:
|
||||
if (last_i == i+1 or i+1 < self.num_full_size_windows) and needs_borders_map[groups[i+1].id]:
|
||||
yield no_borders
|
||||
else:
|
||||
yield no_borders if i == last_i else self.only_main_border
|
||||
@ -157,7 +163,7 @@ class Tall(Layout):
|
||||
if i == last_i:
|
||||
yield no_borders
|
||||
break
|
||||
if needs_borders_map[windows[i+1].id]:
|
||||
if needs_borders_map[groups[i+1].id]:
|
||||
yield no_borders
|
||||
else:
|
||||
yield self.only_between_border
|
||||
@ -172,18 +178,21 @@ class Fat(Tall):
|
||||
main_axis_layout = Layout.ylayout
|
||||
perp_axis_layout = Layout.xlayout
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
idx = windows.index(window)
|
||||
prev = None if idx == 0 else windows[idx-1]
|
||||
nxt = None if idx == len(windows) - 1 else windows[idx+1]
|
||||
ans: InternalNeighborsMap = {'left': [], 'right': [], 'top': [] if prev is None else [prev.id], 'bottom': []}
|
||||
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
||||
wg = all_windows.group_for_window(window)
|
||||
assert wg is not None
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
idx = groups.index(wg)
|
||||
prev = None if idx == 0 else groups[idx-1]
|
||||
nxt = None if idx == len(groups) - 1 else groups[idx+1]
|
||||
ans: NeighborsMap = {'left': [], 'right': [], 'top': [] if prev is None else [prev.id], 'bottom': []}
|
||||
if idx < self.num_full_size_windows - 1:
|
||||
if nxt is not None:
|
||||
ans['bottom'] = [nxt.id]
|
||||
elif idx == self.num_full_size_windows - 1:
|
||||
ans['bottom'] = [w.id for w in windows[idx+1:]]
|
||||
ans['bottom'] = [w.id for w in groups[idx+1:]]
|
||||
else:
|
||||
ans['top'] = [windows[self.num_full_size_windows - 1].id]
|
||||
ans['top'] = [groups[self.num_full_size_windows - 1].id]
|
||||
if idx > self.num_full_size_windows and prev is not None:
|
||||
ans['left'] = [prev.id]
|
||||
if nxt is not None:
|
||||
|
||||
@ -8,7 +8,7 @@ from kitty.typing import WindowType
|
||||
from kitty.window_list import WindowList
|
||||
|
||||
from .base import (
|
||||
Borders, InternalNeighborsMap, Layout, LayoutDimension, ListOfWindows,
|
||||
Borders, NeighborsMap, Layout, LayoutDimension, ListOfWindows,
|
||||
all_borders, no_borders, variable_bias
|
||||
)
|
||||
|
||||
@ -72,7 +72,7 @@ class Vertical(Layout):
|
||||
else:
|
||||
yield self.only_between_border
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
idx = windows.index(window)
|
||||
before = [] if window is windows[0] else [windows[idx-1].id]
|
||||
after = [] if window is windows[-1] else [windows[idx+1].id]
|
||||
|
||||
@ -188,11 +188,6 @@ class Tab: # {{{
|
||||
def on_bell(self, window: Window) -> None:
|
||||
self.mark_tab_bar_dirty()
|
||||
|
||||
def visible_windows(self) -> Generator[Window, None, None]:
|
||||
for w in self.windows:
|
||||
if w.is_visible_in_layout:
|
||||
yield w
|
||||
|
||||
def relayout(self) -> None:
|
||||
if self.windows:
|
||||
self.current_layout(self.windows)
|
||||
@ -201,13 +196,12 @@ class Tab: # {{{
|
||||
def relayout_borders(self) -> None:
|
||||
tm = self.tab_manager_ref()
|
||||
if tm is not None:
|
||||
visible_windows = list(self.visible_windows())
|
||||
w = self.active_window
|
||||
ly = self.current_layout
|
||||
self.borders(
|
||||
windows=visible_windows, active_window=w,
|
||||
all_windows=self.windows,
|
||||
current_layout=ly, extra_blank_rects=tm.blank_rects,
|
||||
draw_window_borders=(ly.needs_window_borders and len(visible_windows) > 1) or ly.must_draw_borders
|
||||
draw_window_borders=(ly.needs_window_borders and self.windows.num_visble_groups > 1) or ly.must_draw_borders
|
||||
)
|
||||
if w is not None:
|
||||
w.change_titlebar_color()
|
||||
@ -420,9 +414,9 @@ class Tab: # {{{
|
||||
neighbors = self.current_layout.neighbors(self.windows)
|
||||
candidates = cast(Optional[Tuple[int, ...]], neighbors.get(which))
|
||||
if candidates:
|
||||
self.windows.set_active_window_group_for(candidates[0])
|
||||
self.windows.set_active_group(candidates[0])
|
||||
|
||||
def move_window(self, delta: int = 1) -> None:
|
||||
def move_window(self, delta: Union[str, int] = 1) -> None:
|
||||
if self.current_layout.move_window(self.windows, delta):
|
||||
self.relayout()
|
||||
|
||||
|
||||
@ -44,6 +44,13 @@ class WindowGroup:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def needs_attention(self) -> bool:
|
||||
for w in self.windows:
|
||||
if w.needs_attention:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def base_window_id(self) -> int:
|
||||
return self.windows[0].id if self.windows else 0
|
||||
@ -71,10 +78,39 @@ class WindowGroup:
|
||||
w = self.windows[0]
|
||||
return w.effective_margin(which, is_single_window=is_single_window) + w.effective_border() * border_mult + w.effective_padding(which)
|
||||
|
||||
def effective_padding(self, which: EdgeLiteral) -> int:
|
||||
if not self.windows:
|
||||
return 0
|
||||
w = self.windows[0]
|
||||
return w.effective_padding(which)
|
||||
|
||||
def effective_border(self) -> int:
|
||||
if not self.windows:
|
||||
return 0
|
||||
w = self.windows[0]
|
||||
return w.effective_border()
|
||||
|
||||
def set_geometry(self, geom: WindowGeometry) -> None:
|
||||
for w in self.windows:
|
||||
w.set_geometry(geom)
|
||||
|
||||
@property
|
||||
def default_bg(self) -> int:
|
||||
if self.windows:
|
||||
return self.windows[-1].screen.color_profile.default_bg
|
||||
return 0
|
||||
|
||||
@property
|
||||
def geometry(self) -> Optional[WindowGeometry]:
|
||||
if self.windows:
|
||||
return self.windows[-1].geometry
|
||||
|
||||
@property
|
||||
def is_visible_in_layout(self) -> bool:
|
||||
if not self.windows:
|
||||
return False
|
||||
return self.windows[-1].is_visible_in_layout
|
||||
|
||||
|
||||
class WindowList:
|
||||
|
||||
@ -149,6 +185,11 @@ class WindowList:
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
def set_active_group(self, group_id: int) -> bool:
|
||||
for i, gr in enumerate(self.groups):
|
||||
if gr.id == group_id:
|
||||
return self.set_active_group_idx(i)
|
||||
|
||||
def change_tab(self, tab: TabType) -> None:
|
||||
self.tabref = weakref.ref(tab)
|
||||
|
||||
@ -158,8 +199,8 @@ class WindowList:
|
||||
for window in g:
|
||||
yield window, window.id == aw
|
||||
|
||||
def iter_all_layoutable_groups(self) -> Iterator[WindowGroup]:
|
||||
return iter(self.groups)
|
||||
def iter_all_layoutable_groups(self, only_visible: bool = False) -> Iterator[WindowGroup]:
|
||||
return iter(g for g in self.groups if g.is_visible_in_layout) if only_visible else iter(self.groups)
|
||||
|
||||
def make_previous_group_active(self, which: int = 1, notify: bool = False) -> None:
|
||||
which = max(1, which)
|
||||
@ -186,6 +227,12 @@ class WindowList:
|
||||
if q in g:
|
||||
return g
|
||||
|
||||
def group_idx_for_window(self, x: WindowOrId) -> Optional[int]:
|
||||
q = self.id_map[x] if isinstance(x, int) else x
|
||||
for i, g in enumerate(self.groups):
|
||||
if q in g:
|
||||
return i
|
||||
|
||||
def windows_in_group_of(self, x: WindowOrId) -> Iterator[WindowType]:
|
||||
g = self.group_for_window(x)
|
||||
if g is not None:
|
||||
@ -289,16 +336,15 @@ class WindowList:
|
||||
def activate_next_window_group(self, delta: int) -> None:
|
||||
self.set_active_group_idx(wrap_increment(self.active_group_idx, self.num_groups, delta))
|
||||
|
||||
def move_window_group(self, by: Optional[int] = None, to_group_with_window_id: Optional[int] = None) -> bool:
|
||||
def move_window_group(self, by: Optional[int] = None, to_group: Optional[int] = None) -> bool:
|
||||
if self.active_group_idx < 0 or not self.groups:
|
||||
return False
|
||||
target = -1
|
||||
if by is not None:
|
||||
target = wrap_increment(self.active_group_idx, self.num_groups, by)
|
||||
if to_group_with_window_id is not None:
|
||||
q = self.id_map[to_group_with_window_id]
|
||||
if to_group is not None:
|
||||
for i, group in enumerate(self.groups):
|
||||
if q in group:
|
||||
if group.id == to_group:
|
||||
target = i
|
||||
break
|
||||
if target > -1:
|
||||
@ -308,3 +354,15 @@ class WindowList:
|
||||
self.set_active_group_idx(target)
|
||||
return True
|
||||
return False
|
||||
|
||||
def compute_needs_borders_map(self, draw_active_borders: bool) -> Dict[int, bool]:
|
||||
ag = self.active_group
|
||||
return {gr.id: ((gr is ag and draw_active_borders) or gr.needs_attention) for gr in self.groups}
|
||||
|
||||
@property
|
||||
def num_visble_groups(self) -> int:
|
||||
ans = 0
|
||||
for gr in self.groups:
|
||||
if gr.is_visible_in_layout:
|
||||
ans += 1
|
||||
return ans
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user