More work on refactoring window groups

This commit is contained in:
Kovid Goyal
2020-05-05 08:08:15 +05:30
parent 50d9718c68
commit e9c4d540b1
9 changed files with 265 additions and 235 deletions

View File

@@ -9,7 +9,7 @@ from functools import partial
from operator import attrgetter
from typing import (
Any, Deque, Dict, Generator, Iterator, List, NamedTuple, Optional, Pattern,
Sequence, Tuple, cast
Sequence, Tuple, Union, cast
)
from .borders import Borders
@@ -19,12 +19,10 @@ from .constants import appname, is_macos, is_wayland
from .fast_data_types import (
add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty,
next_window_id, remove_tab, remove_window, ring_bell, set_active_tab,
swap_tabs, sync_os_window_title, x11_window_id
set_active_window, swap_tabs, sync_os_window_title, x11_window_id
)
from .layout.base import Layout, Rect
from .layout.interface import (
create_layout_object_for, evict_cached_layouts
)
from .layout.interface import create_layout_object_for, evict_cached_layouts
from .options_stub import Options
from .tab_bar import TabBar, TabBarData
from .typing import SessionTab, SessionType, TypedDict
@@ -82,18 +80,16 @@ class Tab: # {{{
cwd_from: Optional[int] = None,
no_initial_window: bool = False
):
self._active_window_idx = 0
self.tab_manager_ref = weakref.ref(tab_manager)
self.os_window_id: int = tab_manager.os_window_id
self.id: int = add_tab(self.os_window_id)
self.active_window_history: Deque[int] = deque()
if not self.id:
raise Exception('No OS window with id {} found, or tab counter has wrapped'.format(self.os_window_id))
self.opts, self.args = tab_manager.opts, tab_manager.args
self.name = getattr(session_tab, 'name', '')
self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or self.opts.enabled_layouts]
self.borders = Borders(self.os_window_id, self.id, self.opts)
self.windows = WindowList()
self.windows = WindowList(self)
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
setattr(self, which + '_window', partial(self.nth_window, num=i))
self._last_used_layout: Optional[str] = None
@@ -121,15 +117,11 @@ class Tab: # {{{
if other_tab._current_layout_name:
self._set_current_layout(other_tab._current_layout_name)
self._last_used_layout = other_tab._last_used_layout
orig_history = deque(other_tab.active_window_history)
orig_active = other_tab._active_window_idx
for window in other_tab.windows:
detach_window(other_tab.os_window_id, other_tab.id, window.id)
other_tab._active_window_idx = 0
self.active_window_history = orig_history
self.windows = other_tab.windows
other_tab.windows = WindowList()
self._active_window_idx = orig_active
self.windows.change_tab(self)
other_tab.windows = WindowList(other_tab)
for window in self.windows:
window.change_tab(self)
attach_window(self.os_window_id, self.id, window.id)
@@ -147,38 +139,23 @@ class Tab: # {{{
self.new_special_window(cmd)
else:
self.new_window(cmd=cmd)
self.set_active_window_idx(session_tab.active_window_idx)
self.windows.active_window = self.windows.all_windows[session_tab.active_window_idx]
def serialize_state(self) -> Dict[str, Any]:
return {
'version': 1,
'id': self.id,
'active_window_idx': self.active_window_idx,
'windows': [w.serialize_state() for w in self],
'window_list': self.windows.serialize_state(),
'current_layout': self._current_layout_name,
'last_used_layout': self._last_used_layout,
'active_window_history': list(self.active_window_history),
'name': self.name,
}
@property
def active_window_idx(self) -> int:
return self._active_window_idx
@active_window_idx.setter
def active_window_idx(self, val: int) -> None:
old_active_window = self.windows.active_window_for_idx(self._active_window_idx)
if old_active_window is not None:
add_active_id_to_history(self.active_window_history, self.windows.overlaid_window_for(old_active_window))
self._active_window_idx = max(0, min(val, self.windows.max_active_idx))
new_active_window = self.windows.active_window_for_idx(self._active_window_idx)
if old_active_window is not new_active_window:
if old_active_window is not None:
old_active_window.focus_changed(False)
if new_active_window is not None:
new_active_window.focus_changed(True)
self.relayout_borders()
self.mark_tab_bar_dirty()
def active_window_changed(self) -> None:
w = self.active_window
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()
def mark_tab_bar_dirty(self) -> None:
tm = self.tab_manager_ref()
@@ -187,7 +164,7 @@ class Tab: # {{{
@property
def active_window(self) -> Optional[Window]:
return self.windows.active_window_for_idx(self.active_window_idx)
return self.windows.active_window
@property
def title(self) -> str:
@@ -195,9 +172,7 @@ class Tab: # {{{
def set_title(self, title: str) -> None:
self.name = title or ''
tm = self.tab_manager_ref()
if tm is not None:
tm.mark_tab_bar_dirty()
self.mark_tab_bar_dirty()
def title_changed(self, window: Window) -> None:
if window is self.active_window:
@@ -206,10 +181,7 @@ class Tab: # {{{
tm.title_changed(self)
def on_bell(self, window: Window) -> None:
tm = self.tab_manager_ref()
if tm is not None:
self.relayout_borders()
tm.mark_tab_bar_dirty()
self.mark_tab_bar_dirty()
def visible_windows(self) -> Generator[Window, None, None]:
for w in self.windows:
@@ -218,7 +190,7 @@ class Tab: # {{{
def relayout(self) -> None:
if self.windows:
self.active_window_idx = self.current_layout(self.windows, self.active_window_idx)
self.current_layout(self.windows)
self.relayout_borders()
def relayout_borders(self) -> None:
@@ -287,12 +259,10 @@ class Tab: # {{{
self.relayout()
def layout_action(self, action_name: str, args: Sequence[str]) -> None:
ret = self.current_layout.layout_action(action_name, args, self.windows, self.active_window_idx)
ret = self.current_layout.layout_action(action_name, args, self.windows)
if ret is None:
ring_bell()
return
if not isinstance(ret, bool) and isinstance(ret, int):
self.active_window_idx = ret
self.relayout()
def launch_child(
@@ -324,8 +294,8 @@ class Tab: # {{{
ans.fork()
return ans
def _add_window(self, window: Window, location: Optional[str] = None) -> None:
self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx, location)
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()
@@ -351,11 +321,9 @@ class Tab: # {{{
self, child, self.opts, self.args, override_title=override_title,
copy_colors_from=copy_colors_from, watchers=watchers
)
if overlay_for is not None:
window.overlay_for = overlay_for
# Must add child before laying out so that resize_pty succeeds
get_boss().add_child(window)
self._add_window(window, location=location)
self._add_window(window, location=location, overlay_for=overlay_for)
if marker:
try:
window.set_marker(marker)
@@ -391,39 +359,12 @@ class Tab: # {{{
if window is not active_window:
self.remove_window(window)
def previous_active_window_idx(self, num: int) -> Optional[int]:
try:
old_window_id = self.active_window_history[-num]
except IndexError:
return None
return self.windows.idx_for_window(old_window_id)
def remove_window(self, window: Window, destroy: bool = True) -> None:
next_window_id = self.windows.next_id_in_stack_on_remove(window)
if next_window_id is None:
try:
next_window_id = self.active_window_history[-1]
except IndexError:
pass
active_window_idx = self.current_layout.remove_window(self.windows, window, self.active_window_idx)
self.windows.remove_window(window)
if destroy:
remove_window(self.os_window_id, self.id, window.id)
else:
detach_window(self.os_window_id, self.id, window.id)
if next_window_id is None:
w = self.windows.active_window_for_idx(active_window_idx)
if w is not None:
next_window_id = w.id
if next_window_id is not None:
for idx, window in enumerate(self.windows):
if window.id == next_window_id:
self.active_window_idx = self.current_layout.set_active_window(self.windows, idx)
break
else:
self.active_window_idx = active_window_idx
else:
self.active_window_idx = active_window_idx
self.relayout_borders()
self.mark_tab_bar_dirty()
active_window = self.active_window
@@ -431,7 +372,7 @@ class Tab: # {{{
self.title_changed(active_window)
def detach_window(self, window: Window) -> Tuple[Window, ...]:
windows = list(self.windows.iter_stack_for_window(window))
windows = list(self.windows.windows_in_group_of(window))
windows.sort(key=attrgetter('id')) # since ids increase in order of creation
for w in reversed(windows):
self.remove_window(w, destroy=False)
@@ -442,15 +383,9 @@ class Tab: # {{{
attach_window(self.os_window_id, self.id, window.id)
self._add_window(window)
def set_active_window_idx(self, idx: int) -> None:
if idx != self.active_window_idx:
self.active_window_idx = self.current_layout.set_active_window(self.windows, idx)
self.relayout_borders()
def set_active_window(self, window: Window) -> None:
idx = self.windows.idx_for_window(window)
if idx is not None:
self.set_active_window_idx(idx)
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
def get_nth_window(self, n: int) -> Optional[Window]:
if self.windows:
@@ -459,17 +394,14 @@ class Tab: # {{{
def nth_window(self, num: int = 0) -> None:
if self.windows:
if num < 0:
idx = self.previous_active_window_idx(-num)
if idx is None:
return
self.active_window_idx = self.current_layout.set_active_window(self.windows, idx)
self.windows.make_previous_group_active(-num)
else:
self.active_window_idx = self.current_layout.activate_nth_window(self.windows, num)
self.current_layout.activate_nth_window(self.windows, num)
self.relayout_borders()
def _next_window(self, delta: int = 1) -> None:
if len(self.windows) > 1:
self.active_window_idx = self.current_layout.next_window(self.windows, self.active_window_idx, delta)
self.current_layout.next_window(self.windows, delta)
self.relayout_borders()
def next_window(self) -> None:
@@ -481,18 +413,20 @@ class Tab: # {{{
prev_window = previous_window
def neighboring_window(self, which: str) -> None:
neighbors = self.current_layout.neighbors(self.windows, self.active_window_idx)
neighbors = self.current_layout.neighbors(self.windows)
candidates = cast(Optional[Tuple[int, ...]], neighbors.get(which))
if candidates:
self.active_window_idx = self.current_layout.set_active_window(self.windows, candidates[0])
self.current_layout.set_active_window(self.windows, candidates[0])
self.relayout_borders()
def move_window(self, delta: int = 1) -> None:
self.active_window_idx = self.current_layout.move_window(self.windows, self.active_window_idx, delta)
self.current_layout.move_window(self.windows, delta)
self.relayout()
def move_window_to_top(self) -> None:
self.move_window(-self.active_window_idx)
n = self.windows.num_groups
if n > 1:
self.move_window(1 - n)
def move_window_forward(self) -> None:
self.move_window()
@@ -524,7 +458,7 @@ class Tab: # {{{
evict_cached_layouts(self.id)
for w in self.windows:
w.destroy()
self.windows = WindowList()
self.windows = WindowList(self)
def __repr__(self) -> str:
return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self)))
@@ -679,7 +613,7 @@ class TabManager: # {{{
'title': tab.name or tab.title,
'layout': str(tab.current_layout.name),
'windows': list(tab.list_windows(active_window)),
'active_window_history': list(tab.active_window_history),
'active_window_history': list(tab.windows.active_window_history),
}
def serialize_state(self) -> Dict[str, Any]: