More work on refactoring window groups, stack layout works
This commit is contained in:
parent
e9c4d540b1
commit
81b28bc1bd
@ -4,9 +4,10 @@
|
||||
|
||||
from functools import partial
|
||||
from itertools import repeat
|
||||
from operator import attrgetter
|
||||
from typing import (
|
||||
Dict, FrozenSet, Generator, Iterable, List, NamedTuple, Optional, Sequence,
|
||||
Tuple, Union, cast
|
||||
Dict, Generator, Iterable, List, NamedTuple, Optional, Sequence, Tuple,
|
||||
Union, cast
|
||||
)
|
||||
|
||||
from kitty.constants import Edges, WindowGeometry
|
||||
@ -212,7 +213,6 @@ class Layout:
|
||||
name: Optional[str] = None
|
||||
needs_window_borders = True
|
||||
must_draw_borders = False # can be overridden to customize behavior from kittens
|
||||
needs_all_windows = False
|
||||
layout_opts = LayoutOpts({})
|
||||
only_active_window_visible = False
|
||||
|
||||
@ -234,17 +234,14 @@ class Layout:
|
||||
return (lgd.cell_width + 1) / lgd.central.width
|
||||
return (lgd.cell_height + 1) / lgd.central.height
|
||||
|
||||
def apply_bias(self, idx: int, increment: float, top_level_windows: ListOfWindows, is_horizontal: bool = True) -> bool:
|
||||
def apply_bias(self, window_id: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
return False
|
||||
|
||||
def remove_all_biases(self) -> bool:
|
||||
return False
|
||||
|
||||
def modify_size_of_window(self, all_windows: WindowList, window_id: int, increment: float, is_horizontal: bool = True) -> bool:
|
||||
idx = all_windows.idx_for_window(window_id)
|
||||
if idx is None:
|
||||
return False
|
||||
return self.apply_bias(idx, increment, list(all_windows.iter_top_level_windows()), is_horizontal)
|
||||
return self.apply_bias(window_id, increment, all_windows, is_horizontal)
|
||||
|
||||
def parse_layout_opts(self, layout_opts: Optional[str] = None) -> LayoutOpts:
|
||||
data: Dict[str, str] = {}
|
||||
@ -256,199 +253,84 @@ class Layout:
|
||||
return type(self.layout_opts)(data)
|
||||
|
||||
def nth_window(self, all_windows: WindowList, num: int) -> Optional[WindowType]:
|
||||
return all_windows.active_window_for_idx(num, clamp=True)
|
||||
return all_windows.active_window_in_nth_group(num, clamp=True)
|
||||
|
||||
def activate_nth_window(self, all_windows: WindowList, num: int) -> int:
|
||||
w = self.nth_window(all_windows, num)
|
||||
assert w is not None
|
||||
active_window_idx = all_windows.idx_for_window(w)
|
||||
assert active_window_idx is not None
|
||||
return self.set_active_window(all_windows, active_window_idx)
|
||||
def activate_nth_window(self, all_windows: WindowList, num: int) -> None:
|
||||
all_windows.set_active_group_idx(num)
|
||||
|
||||
def next_window(self, all_windows: WindowList, active_window_idx: int, delta: int = 1) -> int:
|
||||
w = self.nth_window(all_windows, active_window_idx)
|
||||
assert w is not None
|
||||
idx = all_windows.idx_for_window(w)
|
||||
assert idx is not None
|
||||
num_slots = all_windows.max_active_idx + 1
|
||||
aidx = (idx + num_slots + delta) % num_slots
|
||||
return self.set_active_window(all_windows, aidx)
|
||||
def next_window(self, all_windows: WindowList, delta: int = 1) -> None:
|
||||
all_windows.activate_next_window_group(delta)
|
||||
|
||||
def neighbors(self, all_windows: WindowList) -> NeighborsMap:
|
||||
w = all_windows.active_window_for_idx(active_window_idx)
|
||||
w = all_windows.active_window
|
||||
assert w is not None
|
||||
n = self.neighbors_for_window(w, all_windows)
|
||||
|
||||
def as_indices(windows: Iterable[int]) -> Generator[int, None, None]:
|
||||
for w in windows:
|
||||
idx = all_windows.idx_for_window(w)
|
||||
if idx is not None:
|
||||
yield idx
|
||||
|
||||
id_getter = attrgetter('id')
|
||||
ans: NeighborsMap = {
|
||||
'left': tuple(as_indices(n['left'])),
|
||||
'top': tuple(as_indices(n['top'])),
|
||||
'right': tuple(as_indices(n['right'])),
|
||||
'bottom': tuple(as_indices(n['bottom']))
|
||||
'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, active_window_idx: int, delta: Union[str, int] = 1) -> int:
|
||||
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(windows) < 2 or not delta:
|
||||
return active_window_idx
|
||||
wgd = WindowGroupingData(all_windows)
|
||||
w = wgd.base_window_for_idx(active_window_idx)
|
||||
if w is None:
|
||||
return active_window_idx
|
||||
if len(all_windows) < 2 or not delta:
|
||||
return False
|
||||
|
||||
idx = idx_for_id(w.id, windows)
|
||||
if idx is None and w.overlay_window_id is not None:
|
||||
idx = idx_for_id(w.overlay_window_id, windows)
|
||||
assert idx is not None
|
||||
if isinstance(delta, int):
|
||||
nidx = (idx + len(windows) + delta) % len(windows)
|
||||
else:
|
||||
delta = delta.lower()
|
||||
delta = {'up': 'top', 'down': 'bottom'}.get(delta, delta)
|
||||
neighbors = self.neighbors_for_window(w, all_windows if self.needs_all_windows else windows)
|
||||
q = cast(WindowList, neighbors.get(cast(str, delta), ()))
|
||||
if not q:
|
||||
return active_window_idx
|
||||
w = q[0]
|
||||
qidx = idx_for_id(getattr(w, 'id', w), windows)
|
||||
assert qidx is not None
|
||||
nidx = qidx
|
||||
return all_windows.move_window_group(by=delta)
|
||||
which = delta.lower()
|
||||
which = {'up': 'top', 'down': 'bottom'}.get(which, which)
|
||||
w = all_windows.active_window
|
||||
if w is None:
|
||||
return False
|
||||
neighbors = self.neighbors_for_window(w, all_windows)
|
||||
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])
|
||||
|
||||
nw = windows[nidx]
|
||||
qidx = idx_for_id(nw.id, all_windows)
|
||||
assert qidx is not None
|
||||
nidx = qidx
|
||||
idx = active_window_idx
|
||||
self.swap_windows_in_layout(all_windows, nidx, idx)
|
||||
return self.set_active_window(all_windows, nidx)
|
||||
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:
|
||||
all_windows.add_window(window, group_of=overlay_for)
|
||||
return
|
||||
if location == 'neighbor':
|
||||
location = 'after'
|
||||
self.add_non_overlay_window(all_windows, window, location)
|
||||
|
||||
def swap_windows_in_layout(self, all_windows: WindowList, a: int, b: int) -> None:
|
||||
all_windows[a], all_windows[b] = all_windows[b], all_windows[a]
|
||||
|
||||
def add_window(self, all_windows: WindowList, window: WindowType, location: Optional[str] = None, overlay_for: Optional[int] = None) -> int:
|
||||
active_window_idx = None
|
||||
if window.overlay_for is not None:
|
||||
i = idx_for_id(window.overlay_for, all_windows)
|
||||
if i is not None:
|
||||
# put the overlay window in the position occupied by the
|
||||
# overlaid window and move the overlaid window to the end
|
||||
all_windows.append(all_windows[i])
|
||||
all_windows[i] = window
|
||||
active_window_idx = i
|
||||
if active_window_idx is None:
|
||||
if location == 'neighbor':
|
||||
location = 'after'
|
||||
active_window_idx = self.do_add_window(all_windows, window, current_active_window_idx, location)
|
||||
|
||||
self(all_windows, active_window_idx)
|
||||
self.set_active_window_in_os_window(active_window_idx)
|
||||
return active_window_idx
|
||||
|
||||
def do_add_window(self, all_windows: WindowList, window: WindowType, current_active_window_idx: Optional[int], location: Optional[str]) -> int:
|
||||
active_window_idx = None
|
||||
def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, location: Optional[str]) -> int:
|
||||
next_to: Optional[WindowType] = None
|
||||
before = False
|
||||
next_to = all_windows.active_window
|
||||
if location is not None:
|
||||
if location in ('after', 'vsplit', 'hsplit') and current_active_window_idx is not None and len(all_windows) > 1:
|
||||
active_window_idx = min(current_active_window_idx + 1, len(all_windows))
|
||||
elif location == 'before' and current_active_window_idx is not None and len(all_windows) > 1:
|
||||
active_window_idx = current_active_window_idx
|
||||
if location in ('after', 'vsplit', 'hsplit'):
|
||||
pass
|
||||
elif location == 'before':
|
||||
before = True
|
||||
elif location == 'first':
|
||||
active_window_idx = 0
|
||||
if active_window_idx is not None:
|
||||
all_windows.insert(active_window_idx, window)
|
||||
before = True
|
||||
next_to = None
|
||||
elif location == 'last':
|
||||
next_to = None
|
||||
all_windows.add_window(window, next_to=next_to, before=before)
|
||||
|
||||
if active_window_idx is None:
|
||||
active_window_idx = len(all_windows)
|
||||
all_windows.append(window)
|
||||
return active_window_idx
|
||||
|
||||
def remove_window(self, all_windows: WindowList, window: WindowType, current_active_window_idx: int, swapped: bool = False) -> int:
|
||||
try:
|
||||
active_window = all_windows[current_active_window_idx]
|
||||
except Exception:
|
||||
active_window = window
|
||||
if not swapped and window.overlay_for is not None:
|
||||
nidx = idx_for_id(window.overlay_for, all_windows)
|
||||
if nidx is not None:
|
||||
idx = all_windows.index(window)
|
||||
all_windows[nidx], all_windows[idx] = all_windows[idx], all_windows[nidx]
|
||||
return self.remove_window(all_windows, window, current_active_window_idx, swapped=True)
|
||||
|
||||
position = all_windows.index(window)
|
||||
del all_windows[position]
|
||||
active_window_idx = None
|
||||
if window.overlay_for is not None:
|
||||
i = idx_for_id(window.overlay_for, all_windows)
|
||||
if i is not None:
|
||||
overlaid_window = all_windows[i]
|
||||
overlaid_window.overlay_window_id = None
|
||||
if active_window is window:
|
||||
active_window = overlaid_window
|
||||
active_window_idx = idx_for_id(active_window.id, all_windows)
|
||||
if active_window_idx is None:
|
||||
if active_window is window:
|
||||
active_window_idx = max(0, min(current_active_window_idx, len(all_windows) - 1))
|
||||
else:
|
||||
active_window_idx = idx_for_id(active_window.id, all_windows)
|
||||
assert active_window_idx is not None
|
||||
if all_windows:
|
||||
self(all_windows, active_window_idx)
|
||||
return self.set_active_window(all_windows, active_window_idx)
|
||||
|
||||
def update_visibility(self, all_windows: WindowList, active_window: WindowType, overlaid_windows: Optional[FrozenSet[WindowType]] = None) -> None:
|
||||
if overlaid_windows is None:
|
||||
overlaid_windows = process_overlaid_windows(all_windows)[0]
|
||||
for i, w in enumerate(all_windows):
|
||||
w.set_visible_in_layout(i, w is active_window or (not self.only_active_window_visible and w not in overlaid_windows))
|
||||
|
||||
def set_active_window(self, all_windows: WindowList, active_window_idx: int) -> int:
|
||||
if not all_windows:
|
||||
self.set_active_window_in_os_window(0)
|
||||
return 0
|
||||
w = all_windows[active_window_idx]
|
||||
if w.overlay_window_id is not None:
|
||||
i = idx_for_id(w.overlay_window_id, all_windows)
|
||||
if i is not None:
|
||||
active_window_idx = i
|
||||
self.update_visibility(all_windows, all_windows[active_window_idx])
|
||||
self.set_active_window_in_os_window(active_window_idx)
|
||||
return active_window_idx
|
||||
def update_visibility(self, all_windows: WindowList) -> None:
|
||||
active_window = all_windows.active_window
|
||||
for window, is_group_leader in all_windows.iter_windows_with_visibility():
|
||||
is_visible = window is active_window or (is_group_leader and not self.only_active_window_visible)
|
||||
window.set_visible_in_layout(is_visible)
|
||||
|
||||
def _set_dimensions(self) -> None:
|
||||
lgd.central, tab_bar, vw, vh, lgd.cell_width, lgd.cell_height = viewport_for_window(self.os_window_id)
|
||||
|
||||
def __call__(self, all_windows: WindowList) -> int:
|
||||
def __call__(self, all_windows: WindowList) -> None:
|
||||
self._set_dimensions()
|
||||
active_window = all_windows[active_window_idx]
|
||||
overlaid_windows, windows = process_overlaid_windows(all_windows)
|
||||
if overlaid_windows:
|
||||
windows = [w for w in all_windows if w not in overlaid_windows]
|
||||
q = idx_for_id(active_window.id, windows)
|
||||
if q is None:
|
||||
if active_window.overlay_window_id is not None:
|
||||
active_window_idx = idx_for_id(active_window.overlay_window_id, windows) or 0
|
||||
else:
|
||||
active_window_idx = 0
|
||||
else:
|
||||
active_window_idx = q
|
||||
active_window = windows[active_window_idx]
|
||||
else:
|
||||
windows = all_windows
|
||||
self.update_visibility(all_windows, active_window, overlaid_windows)
|
||||
self.update_visibility(all_windows)
|
||||
self.blank_rects = []
|
||||
if self.needs_all_windows:
|
||||
self.do_layout_all_windows(windows, active_window_idx, all_windows)
|
||||
else:
|
||||
self.do_layout(windows, active_window_idx)
|
||||
return cast(int, idx_for_id(active_window.id, all_windows))
|
||||
|
||||
# Utils {{{
|
||||
self.do_layout(all_windows)
|
||||
|
||||
def layout_single_window(self, w: WindowType, return_geometry: bool = False, left_align: bool = False) -> Optional[WindowGeometry]:
|
||||
bw = w.effective_border() if self.must_draw_borders else 0
|
||||
@ -463,7 +345,7 @@ class Layout:
|
||||
wg = layout_single_window(xdecoration_pairs, ydecoration_pairs, left_align=left_align)
|
||||
if return_geometry:
|
||||
return wg
|
||||
w.set_geometry(0, wg)
|
||||
w.set_geometry(wg)
|
||||
self.blank_rects = list(blank_rects_for_window(wg))
|
||||
return None
|
||||
|
||||
@ -507,15 +389,10 @@ class Layout:
|
||||
|
||||
def set_window_geometry(self, w: WindowType, idx: int, xl: LayoutData, yl: LayoutData) -> None:
|
||||
wg = window_geometry_from_layouts(xl, yl)
|
||||
w.set_geometry(idx, wg)
|
||||
w.set_geometry(wg)
|
||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
||||
|
||||
# }}}
|
||||
|
||||
def do_layout(self, windows: WindowList, active_window_idx: int) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_layout_all_windows(self, windows: WindowList, active_window_idx: int, all_windows: WindowList) -> None:
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||
|
||||
@ -344,11 +344,10 @@ class Splits(Layout):
|
||||
else:
|
||||
root.layout_pair(lgd.central.left, lgd.central.top, lgd.central.width, lgd.central.height, id_window_map, id_idx_map, self)
|
||||
|
||||
def do_add_window(
|
||||
def add_non_overlay_window(
|
||||
self,
|
||||
all_windows: WindowList,
|
||||
window: WindowType,
|
||||
current_active_window_idx: Optional[int],
|
||||
location: Optional[str]
|
||||
) -> int:
|
||||
horizontal = self.default_axis_is_horizontal
|
||||
|
||||
@ -13,10 +13,10 @@ class Stack(Layout):
|
||||
needs_window_borders = False
|
||||
only_active_window_visible = True
|
||||
|
||||
def do_layout(self, windows: WindowList, active_window_idx: int) -> None:
|
||||
for i, w in enumerate(windows):
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
for i, w in enumerate(windows.iter_all_layoutable_windows()):
|
||||
wg = self.layout_single_window(w, left_align=lgd.align_top_left, return_geometry=True)
|
||||
if wg is not None:
|
||||
w.set_geometry(i, wg)
|
||||
if w.is_visible_in_layout:
|
||||
w.set_geometry(wg)
|
||||
if i == 0:
|
||||
self.blank_rects = list(blank_rects_for_window(wg))
|
||||
|
||||
@ -139,7 +139,7 @@ class Tab: # {{{
|
||||
self.new_special_window(cmd)
|
||||
else:
|
||||
self.new_window(cmd=cmd)
|
||||
self.windows.active_window = self.windows.all_windows[session_tab.active_window_idx]
|
||||
self.windows.set_active_window_group_for(self.windows.all_windows[session_tab.active_window_idx])
|
||||
|
||||
def serialize_state(self) -> Dict[str, Any]:
|
||||
return {
|
||||
@ -156,6 +156,7 @@ class Tab: # {{{
|
||||
set_active_window(self.os_window_id, self.id, 0 if w is None else w.id)
|
||||
self.mark_tab_bar_dirty()
|
||||
self.relayout_borders()
|
||||
self.current_layout.update_visibility(self.windows)
|
||||
|
||||
def mark_tab_bar_dirty(self) -> None:
|
||||
tm = self.tab_manager_ref()
|
||||
@ -296,8 +297,8 @@ class Tab: # {{{
|
||||
|
||||
def _add_window(self, window: Window, location: Optional[str] = None, overlay_for: Optional[int] = None) -> None:
|
||||
self.current_layout.add_window(self.windows, window, location, overlay_for)
|
||||
self.relayout_borders()
|
||||
self.mark_tab_bar_dirty()
|
||||
self.relayout()
|
||||
|
||||
def new_window(
|
||||
self,
|
||||
@ -365,8 +366,8 @@ class Tab: # {{{
|
||||
remove_window(self.os_window_id, self.id, window.id)
|
||||
else:
|
||||
detach_window(self.os_window_id, self.id, window.id)
|
||||
self.relayout_borders()
|
||||
self.mark_tab_bar_dirty()
|
||||
self.relayout()
|
||||
active_window = self.active_window
|
||||
if active_window:
|
||||
self.title_changed(active_window)
|
||||
@ -384,8 +385,7 @@ class Tab: # {{{
|
||||
self._add_window(window)
|
||||
|
||||
def set_active_window(self, x: Union[Window, int]) -> None:
|
||||
q = self.windows.id_map[x] if isinstance(x, int) else x
|
||||
self.windows.active_window = q
|
||||
self.windows.set_active_window_group_for(x)
|
||||
|
||||
def get_nth_window(self, n: int) -> Optional[Window]:
|
||||
if self.windows:
|
||||
@ -416,12 +416,11 @@ class Tab: # {{{
|
||||
neighbors = self.current_layout.neighbors(self.windows)
|
||||
candidates = cast(Optional[Tuple[int, ...]], neighbors.get(which))
|
||||
if candidates:
|
||||
self.current_layout.set_active_window(self.windows, candidates[0])
|
||||
self.relayout_borders()
|
||||
self.windows.set_active_window_group_for(candidates[0])
|
||||
|
||||
def move_window(self, delta: int = 1) -> None:
|
||||
self.current_layout.move_window(self.windows, delta)
|
||||
self.relayout()
|
||||
if self.current_layout.move_window(self.windows, delta):
|
||||
self.relayout()
|
||||
|
||||
def move_window_to_top(self) -> None:
|
||||
n = self.windows.num_groups
|
||||
|
||||
@ -6,7 +6,9 @@ import weakref
|
||||
from collections import deque
|
||||
from contextlib import suppress
|
||||
from itertools import count
|
||||
from typing import Any, Deque, Dict, Iterator, List, Optional, Union
|
||||
from typing import (
|
||||
Any, Deque, Dict, Generator, Iterator, List, Optional, Tuple, Union
|
||||
)
|
||||
|
||||
from .typing import TabType, WindowType
|
||||
|
||||
@ -14,6 +16,12 @@ WindowOrId = Union[WindowType, int]
|
||||
group_id_counter = count()
|
||||
|
||||
|
||||
def wrap_increment(val: int, num: int, delta: int) -> int:
|
||||
mult = -1 if delta < 0 else 1
|
||||
delta = mult * (abs(delta) % num)
|
||||
return (val + num + delta) % num
|
||||
|
||||
|
||||
class WindowGroup:
|
||||
|
||||
def __init__(self) -> None:
|
||||
@ -63,7 +71,7 @@ class WindowList:
|
||||
self.all_windows: List[WindowType] = []
|
||||
self.id_map: Dict[int, WindowType] = {}
|
||||
self.groups: List[WindowGroup] = []
|
||||
self.active_group_idx: int = -1
|
||||
self._active_group_idx: int = -1
|
||||
self.active_group_history: Deque[int] = deque((), 64)
|
||||
self.tabref = weakref.ref(tab)
|
||||
|
||||
@ -76,8 +84,9 @@ class WindowList:
|
||||
def __iter__(self) -> Iterator[WindowType]:
|
||||
return iter(self.all_windows)
|
||||
|
||||
def __contains__(self, window: WindowType) -> bool:
|
||||
return window.id in self.id_map
|
||||
def __contains__(self, window: WindowOrId) -> bool:
|
||||
q = window if isinstance(window, int) else window.id
|
||||
return q in self.id_map
|
||||
|
||||
def serialize_state(self) -> Dict[str, Any]:
|
||||
return {
|
||||
@ -86,6 +95,10 @@ class WindowList:
|
||||
'window_groups': [g.serialize_state() for g in self.groups]
|
||||
}
|
||||
|
||||
@property
|
||||
def active_group_idx(self) -> int:
|
||||
return self._active_group_idx
|
||||
|
||||
@property
|
||||
def active_window_history(self) -> List[int]:
|
||||
ans = []
|
||||
@ -100,14 +113,14 @@ class WindowList:
|
||||
return ans
|
||||
|
||||
def set_active_group_idx(self, i: int) -> None:
|
||||
if i != self.active_group_idx and 0 <= i < len(self.groups):
|
||||
if i != self._active_group_idx and 0 <= i < len(self.groups):
|
||||
old_active_window = self.active_window
|
||||
g = self.active_group
|
||||
if g is not None:
|
||||
with suppress(ValueError):
|
||||
self.active_group_history.remove(g.id)
|
||||
self.active_group_history.append(g.id)
|
||||
self.active_group_idx = i
|
||||
self._active_group_idx = i
|
||||
new_active_window = self.active_window
|
||||
if old_active_window is not new_active_window:
|
||||
if old_active_window is not None:
|
||||
@ -121,9 +134,18 @@ class WindowList:
|
||||
def change_tab(self, tab: TabType) -> None:
|
||||
self.tabref = weakref.ref(tab)
|
||||
|
||||
def iter_windows_with_visibility(self) -> Generator[Tuple[WindowType, bool], None, None]:
|
||||
for g in self.groups:
|
||||
aw = g.active_window_id
|
||||
for window in g:
|
||||
yield window, window.id == aw
|
||||
|
||||
def iter_all_layoutable_windows(self) -> Generator[WindowType, None, None]:
|
||||
for g in self.groups:
|
||||
yield from g
|
||||
|
||||
def make_previous_group_active(self, which: int = 1) -> None:
|
||||
which = max(1, which)
|
||||
self.active_group_idx = len(self.groups) - 1
|
||||
gid_map = {g.id: i for i, g in enumerate(self.groups)}
|
||||
num = len(self.active_group_history)
|
||||
for i in range(num):
|
||||
@ -135,6 +157,7 @@ class WindowList:
|
||||
if which < 1:
|
||||
self.set_active_group_idx(x)
|
||||
return
|
||||
self.set_active_group_idx(len(self.groups) - 1)
|
||||
|
||||
@property
|
||||
def num_groups(self) -> int:
|
||||
@ -154,18 +177,21 @@ class WindowList:
|
||||
|
||||
@property
|
||||
def active_group(self) -> Optional[WindowGroup]:
|
||||
if self.active_group_idx >= 0:
|
||||
try:
|
||||
return self.groups[self.active_group_idx]
|
||||
except IndexError:
|
||||
pass
|
||||
return None
|
||||
|
||||
@property
|
||||
def active_window(self) -> Optional[WindowType]:
|
||||
if self.active_group_idx >= 0:
|
||||
try:
|
||||
return self.id_map[self.groups[self.active_group_idx].active_window_id]
|
||||
except IndexError:
|
||||
pass
|
||||
return None
|
||||
|
||||
@active_window.setter
|
||||
def active_window(self, x: WindowOrId) -> None:
|
||||
def set_active_window_group_for(self, x: WindowOrId) -> None:
|
||||
q = self.id_map[x] if isinstance(x, int) else x
|
||||
for i, group in enumerate(self.groups):
|
||||
if q in group:
|
||||
@ -198,7 +224,10 @@ class WindowList:
|
||||
self.groups.insert(pos + (0 if before else 1), target_group)
|
||||
if target_group is None:
|
||||
target_group = WindowGroup()
|
||||
self.groups.append(target_group)
|
||||
if before:
|
||||
self.groups.insert(0, target_group)
|
||||
else:
|
||||
self.groups.append(target_group)
|
||||
|
||||
target_group.add_window(window)
|
||||
if make_active:
|
||||
@ -218,5 +247,38 @@ class WindowList:
|
||||
g.remove_window(q)
|
||||
if not g:
|
||||
del self.groups[i]
|
||||
if self.active_group_idx == i:
|
||||
self.make_previous_group_active()
|
||||
if self.groups:
|
||||
if self.active_group_idx == i:
|
||||
self.make_previous_group_active()
|
||||
else:
|
||||
self._active_group_idx = -1
|
||||
return
|
||||
|
||||
def active_window_in_nth_group(self, n: int, clamp: bool = False) -> Optional[WindowType]:
|
||||
if clamp:
|
||||
n = max(0, min(n, self.num_groups - 1))
|
||||
if 0 <= n < self.num_groups:
|
||||
return self.id_map.get(self.groups[n].active_window_id)
|
||||
|
||||
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:
|
||||
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]
|
||||
for i, group in enumerate(self.groups):
|
||||
if q in group:
|
||||
target = i
|
||||
break
|
||||
if target > -1:
|
||||
if target == self.active_group_idx:
|
||||
return False
|
||||
self.groups[self.active_group_idx], self.groups[target] = self.groups[target], self.groups[self.active_group_idx]
|
||||
self.set_active_group_idx(target)
|
||||
return True
|
||||
return False
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user