kitty/kitty/window_list.py
2020-05-12 22:43:53 +05:30

91 lines
3.5 KiB
Python

#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from typing import Dict, Generator, Iterator, List, Optional, Union
from .typing import WindowType
class WindowList:
def __init__(self) -> None:
self.all_windows: List[WindowType] = []
self.id_map: Dict[int, WindowType] = {}
self.overlay_stacks: Dict[int, List[int]] = {}
self.id_to_idx_map: Dict[int, int] = {}
self.idx_to_base_id_map: Dict[int, int] = {}
self.max_active_idx = 0
def __len__(self) -> int:
return len(self.all_windows)
def __bool__(self) -> bool:
return bool(self.all_windows)
def __iter__(self) -> Iterator[WindowType]:
return iter(self.all_windows)
def __contains__(self, window: WindowType) -> bool:
return window.id in self.id_map
def stack_for_window_id(self, q: int) -> List[int]:
' The stack of overlaid windows this window belongs to '
w = self.id_map[q]
if w.overlay_for is not None and w.overlay_for in self.id_map:
q = self.id_map[w.overlay_for].id
return self.overlay_stacks[q]
def iter_top_level_windows(self) -> Generator[WindowType, None, None]:
' Iterator over all top level windows '
for stack in self.overlay_stacks.values():
yield self.id_map[stack[-1]]
def iter_stack_for_window(self, x: Union[WindowType, int], reverse: bool = False) -> Generator[WindowType, None, None]:
' Iterator over all windows in the stack for this window '
q = x if isinstance(x, int) else x.id
stack = self.stack_for_window_id(q)
y = reversed(stack) if reverse else iter(stack)
for wid in y:
yield self.id_map[wid]
def overlay_for(self, x: Union[WindowType, int]) -> int:
' id of the top-most window overlaying this window, same as this window id if not overlaid '
q = x if isinstance(x, int) else x.id
return self.stack_for_window_id(q)[-1]
def overlaid_window_for(self, x: Union[WindowType, int]) -> int:
' id of the bottom-most window in this windows overlay stack '
q = x if isinstance(x, int) else x.id
return self.stack_for_window_id(q)[0]
def is_overlaid(self, x: Union[WindowType, int]) -> bool:
' Return False if there is a window overlaying this one '
q = x if isinstance(x, int) else x.id
return self.overlay_for(q) != q
def idx_for_window(self, x: Union[WindowType, int]) -> Optional[int]:
' Return the index of the window in the list of top-level windows '
q = x if isinstance(x, int) else x.id
return self.id_to_idx_map[q]
def active_window_for_idx(self, idx: int, clamp: bool = False) -> Optional[WindowType]:
' Return the active window at the specified index '
if clamp:
idx = max(0, min(idx, self.max_active_idx))
q = self.idx_to_base_id_map.get(idx)
if q is not None:
return self.id_map[self.overlay_stacks[q][-1]]
return None
def next_id_in_stack_on_remove(self, x: Union[WindowType, int]) -> Optional[int]:
' The id of the window that should become active when this window is removed, or None if there is no other window in the stack '
q = x if isinstance(x, int) else x.id
stack = self.stack_for_window_id(q)
idx = stack.index(q)
if idx < len(stack) - 1:
return stack[idx + 1]
if idx > 0:
return stack[idx - 1]
return None