Implement unserialization for splits layout

Also cleanup the serialization code.
This commit is contained in:
Kovid Goyal 2025-08-02 12:12:03 +05:30
parent f64182e16c
commit c4ed1d3b69
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
5 changed files with 68 additions and 24 deletions

View file

@ -10,7 +10,7 @@ from kitty.borders import BorderColor
from kitty.fast_data_types import Region, set_active_window, viewport_for_window
from kitty.options.types import Options
from kitty.types import Edges, WindowGeometry
from kitty.typing_compat import TypedDict, WindowType
from kitty.typing_compat import TypedDict, WindowMapper, WindowType
from kitty.window_list import WindowGroup, WindowList
@ -436,3 +436,17 @@ class Layout:
def layout_state(self) -> dict[str, Any]:
return {}
def set_layout_state(self, layout_state: dict[str, Any], map_window_id: WindowMapper) -> bool:
return True
def serialize(self) -> dict[str, Any]:
ans = self.layout_state()
ans['opts'] = self.layout_opts.serialized()
ans['class'] = self.__class__.__name__
return ans
def unserialize(self, s: dict[str, Any], map_window_id: WindowMapper) -> bool:
if s.get('class') != self.__class__.__name__:
return False
return self.set_layout_state(s, map_window_id)

View file

@ -2,11 +2,11 @@
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from collections.abc import Collection, Generator, Sequence
from typing import Any, NamedTuple, Optional, Union
from typing import Any, NamedTuple, Optional, TypedDict, Union
from kitty.borders import BorderColor
from kitty.types import Edges, WindowGeometry
from kitty.typing_compat import EdgeLiteral, WindowType
from kitty.typing_compat import EdgeLiteral, WindowMapper, WindowType
from kitty.window_list import WindowGroup, WindowList
from .base import BorderLine, Layout, LayoutOpts, NeighborsMap, blank_rects_for_window, lgd, window_geometry_from_layouts
@ -17,6 +17,13 @@ class Extent(NamedTuple):
end: int = 0
class SerializedPair(TypedDict, total=False):
horizontal: bool # default to True if absent
bias: float # default to 0.5 if absent
one: Union[int, 'SerializedPair'] # default to None if absent
two: Union[int, 'SerializedPair'] # default to None if absent
class Pair:
def __init__(self, horizontal: bool = True):
@ -28,6 +35,34 @@ class Pair:
self.between_borders: list[Edges] = []
self.first_extent = self.second_extent = Extent()
def serialize(self) -> SerializedPair:
ans: SerializedPair = {}
if not self.horizontal:
ans['horizontal'] = False
if self.bias != 0.5:
ans['bias'] = self.bias
if self.one is not None:
ans['one'] = self.one.serialize() if isinstance(self.one, Pair) else self.one
if self.two is not None:
ans['two'] = self.two.serialize() if isinstance(self.two, Pair) else self.two
return ans
def unserialize(self, s: SerializedPair, map_window_id: WindowMapper) -> None:
self.bias = s.get('bias', 0.5)
self.horizontal = s.get('horizontal', True)
def unserialize(x: int | SerializedPair | None) -> int | Pair | None:
if x is None:
return None
if isinstance(x, int):
w = map_window_id(x)
return None if w is None else w.id
ans = Pair()
ans.unserialize(x, map_window_id)
return ans if ans.one or ans.two else None
self.one = unserialize(s.get('one'))
self.two = unserialize(s.get('two'))
def __repr__(self) -> str:
return 'Pair(horizontal={}, bias={:.2f}, one={}, two={}, between_borders={})'.format(
self.horizontal, self.bias, self.one, self.two, self.between_borders)
@ -673,19 +708,13 @@ class Splits(Layout):
return None
def layout_state(self) -> dict[str, Any]:
return {'pairs': self.pairs_root.serialize(), 'opts': self.layout_opts.serialized()}
def add_pair(p: Pair) -> dict[str, Any]:
ans: dict[str, Any] = {}
ans['horizontal'] = p.horizontal
ans['bias'] = p.bias
if isinstance(p.one, Pair):
ans['one'] = add_pair(p.one)
elif p.one is not None:
ans['one'] = p.one
if isinstance(p.two, Pair):
ans['two'] = add_pair(p.two)
elif p.two is not None:
ans['two'] = p.two
return ans
return {'pairs': add_pair(self.pairs_root)}
def set_layout_state(self, layout_state: dict[str, Any], map_window_id: WindowMapper) -> bool:
new_root = Pair()
new_root.unserialize(layout_state['pairs'], map_window_id)
if new_root.one or new_root.two:
self.pairs_root = new_root
self.layout_opts = SplitsLayoutOpts(layout_state['opts'])
return True
return False

View file

@ -272,8 +272,7 @@ class Tab: # {{{
'window_list': self.windows.serialize_state(),
'current_layout': self._current_layout_name,
'last_used_layout': self._last_used_layout,
'layout_opts': self.current_layout.layout_opts,
'layout_state': self.current_layout.layout_state,
'layout_state': self.current_layout.serialize(),
'enabled_layouts': self.enabled_layouts,
'name': self.name,
}

View file

@ -13,7 +13,7 @@ ScreenSize = KittensKeyActionType = MouseEvent = MouseButton = AbstractEventLoop
TermManagerType = LoopType = Debug = GraphicsCommandType = object
ReadableBuffer = WriteableBuffer = bytearray
CompletedProcess = tuple
CompletedProcess = object
TypedDict = dict
EdgeLiteral = str
UnderlineLiteral = str
@ -21,5 +21,6 @@ PowerlineStyle = str
MatchType = str
Protocol = object
OptionsProtocol = object
NotRequired = tuple
NotRequired = object
CoreTextFont = FontConfigPattern = dict
WindowMapper = object

View file

@ -5,7 +5,7 @@ from socket import AddressFamily as AddressFamily
from socket import socket as Socket
from subprocess import CompletedProcess as CompletedProcess
from subprocess import Popen as PopenType
from typing import Literal
from typing import Callable, Literal
from typing import NotRequired as NotRequired
from typing import Protocol as Protocol
from typing import TypedDict as TypedDict
@ -54,6 +54,7 @@ GRT_C = Literal[0, 1]
GRT_d = Literal['a', 'A', 'c', 'C', 'i', 'I', 'p', 'P', 'q', 'Q', 'x', 'X', 'y', 'Y', 'z', 'Z', 'f', 'F']
ReadableBuffer = bytes | bytearray | memoryview | array.array[int] | mmap.mmap
WriteableBuffer = bytearray | memoryview | array.array[int] | mmap.mmap
WindowMapper = Callable[[int], WindowType | None]
@ -71,5 +72,5 @@ __all__ = (
'KeyActionType', 'KeyMap', 'KittyCommonOpts', 'AliasMap', 'CoreTextFont', 'WindowSystemMouseEvent',
'FontConfigPattern', 'ScreenType', 'StartupCtx', 'KeyEventType', 'LayoutType', 'PowerlineStyle',
'RemoteCommandType', 'SessionType', 'SessionTab', 'SpecialWindowInstance', 'TabType', 'ScreenSize', 'WindowType',
'ReadableBuffer', 'WriteableBuffer',
'ReadableBuffer', 'WriteableBuffer', 'WindowMapper',
)