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