1from abc
import ABC, abstractmethod
2from itertools
import islice
3from operator
import itemgetter
4from threading
import RLock
17from ._ratio
import ratio_resolve
18from .align
import Align
19from .console
import Console, ConsoleOptions, RenderableType, RenderResult
20from .highlighter
import ReprHighlighter
21from .panel
import Panel
22from .pretty
import Pretty
23from .region
import Region
24from .repr
import Result, rich_repr
25from .segment
import Segment
26from .style
import StyleType
33 """An individual layout render."""
36 render: List[List[Segment]]
39RegionMap = Dict[
"Layout", Region]
40RenderMap = Dict[
"Layout", LayoutRender]
44 """Layout related error."""
47class NoSplitter(LayoutError):
48 """Requested splitter does not exist."""
52 """An internal renderable used as a Layout placeholder."""
56 def __init__(self, layout:
"Layout", style: StyleType =
"") ->
None:
61 self, console: Console, options: ConsoleOptions
67 f
"{layout.name!r} ({width} x {height})"
69 else f
"({width} x {height})"
81 """Base class for a splitter."""
87 """Get the icon (emoji) used in layout.tree"""
91 self, children: Sequence[
"Layout"], region: Region
92 ) -> Iterable[Tuple[
"Layout", Region]]:
93 """Divide a region amongst several child layouts.
96 children (Sequence(Layout)): A number of child layouts.
97 region (Region): A rectangular region to divide.
102 """Split a layout region in to rows."""
107 return "[layout.tree.row]⬌"
110 self, children: Sequence[
"Layout"], region: Region
111 ) -> Iterable[Tuple[
"Layout", Region]]:
112 x, y, width, height = region
113 render_widths = ratio_resolve(width, children)
116 for child, child_width
in zip(children, render_widths):
117 yield child,
_Region(x + offset, y, child_width, height)
118 offset += child_width
122 """Split a layout region in to columns."""
127 return "[layout.tree.column]⬍"
130 self, children: Sequence[
"Layout"], region: Region
131 ) -> Iterable[Tuple[
"Layout", Region]]:
132 x, y, width, height = region
133 render_heights = ratio_resolve(height, children)
136 for child, child_height
in zip(children, render_heights):
137 yield child,
_Region(x, y + offset, width, child_height)
138 offset += child_height
143 """A renderable to divide a fixed height in to rows or columns.
146 renderable (RenderableType, optional): Renderable content, or None for placeholder. Defaults to None.
147 name (str, optional): Optional identifier for Layout. Defaults to None.
148 size (int, optional): Optional fixed size of layout. Defaults to None.
149 minimum_size (int, optional): Minimum size of layout. Defaults to 1.
150 ratio (int, optional): Optional ratio for flexible layout. Defaults to 1.
151 visible (bool, optional): Visibility of layout. Defaults to True.
154 splitters = {
"row": RowSplitter,
"column": ColumnSplitter}
158 renderable: Optional[RenderableType] =
None,
160 name: Optional[str] =
None,
161 size: Optional[int] =
None,
162 minimum_size: int = 1,
164 visible: bool =
True,
173 self._children: List[Layout] = []
178 yield "name", self.
name,
None
179 yield "size", self.
size,
None
181 yield "ratio", self.
ratio, 1
185 """Layout renderable."""
186 return self
if self._children
else self.
_renderable
190 """Gets (visible) layout children."""
194 def map(self) -> RenderMap:
195 """Get a map of the last render."""
198 def get(self, name: str) -> Optional[
"Layout"]:
199 """Get a named layout, or None if it doesn't exist.
202 name (str): Name of layout.
205 Optional[Layout]: Layout instance or None if no layout was found.
207 if self.
name == name:
210 for child
in self._children:
212 if named_layout
is not None:
217 layout = self.
get(name)
219 raise KeyError(f
"No layout with name {name!r}")
223 def tree(self) -> "Tree":
224 """Get a tree renderable to show layout structure."""
229 def summary(layout:
"Layout") -> Table:
235 text: RenderableType = (
245 guide_style=f
"layout.tree.{layout.splitter.name}",
249 def recurse(tree:
"Tree", layout:
"Layout") ->
None:
254 guide_style=f
"layout.tree.{child.splitter.name}",
264 *layouts: Union[
"Layout", RenderableType],
265 splitter: Union[Splitter, str] =
"column",
267 """Split the layout in to multiple sub-layouts.
270 *layouts (Layout): Positional arguments should be (sub) Layout instances.
271 splitter (Union[Splitter, str]): Splitter instance or name of splitter.
275 for layout
in layouts
284 raise NoSplitter(f
"No splitter called {splitter!r}")
285 self._children[:] = _layouts
287 def add_split(self, *layouts: Union[
"Layout", RenderableType]) ->
None:
288 """Add a new layout(s) to existing split.
291 *layouts (Union[Layout, RenderableType]): Positional arguments should be renderables or (sub) Layout instances.
296 for layout
in layouts
298 self._children.extend(_layouts)
300 def split_row(self, *layouts: Union[
"Layout", RenderableType]) ->
None:
301 """Split the layout in to a row (layouts side by side).
304 *layouts (Layout): Positional arguments should be (sub) Layout instances.
306 self.
split(*layouts, splitter=
"row")
308 def split_column(self, *layouts: Union[
"Layout", RenderableType]) ->
None:
309 """Split the layout in to a column (layouts stacked on top of each other).
312 *layouts (Layout): Positional arguments should be (sub) Layout instances.
314 self.
split(*layouts, splitter=
"column")
317 """Reset splits to initial state."""
318 del self._children[:]
320 def update(self, renderable: RenderableType) ->
None:
321 """Update renderable.
324 renderable (RenderableType): New renderable object.
330 """Refresh a sub-layout.
333 console (Console): Console instance where Layout is to be rendered.
334 layout_name (str): Name of layout.
337 layout = self[layout_name]
339 (x, y, width, height) = region
347 """Create a dict that maps layout on to Region."""
348 stack: List[Tuple[Layout, Region]] = [(self,
Region(0, 0, width, height))]
351 layout_regions: List[Tuple[Layout, Region]] = []
355 layout, region = layout_regions[-1]
359 push(child_and_region)
363 for layout, region
in sorted(layout_regions, key=itemgetter(1))
367 def render(self, console: Console, options: ConsoleOptions) -> RenderMap:
368 """Render the sub_layouts.
371 console (Console): Console instance.
372 options (ConsoleOptions): Console options.
375 RenderMap: A dict that maps Layout on to a tuple of Region, lines
385 render_map: Dict[
"Layout",
"LayoutRender"] = {}
389 for layout, region
in layout_regions:
390 lines = render_lines(
397 self, console: Console, options: ConsoleOptions
404 layout_lines: List[List[Segment]] = [[]
for _
in range(height)]
407 _x, y, _layout_width, layout_height = region
408 for row, line
in zip(
409 _islice(layout_lines, y, y + layout_height), lines
414 for layout_row
in layout_lines:
415 yield from layout_row
419if __name__ ==
"__main__":
426 Layout(name=
"header", size=3),
427 Layout(ratio=1, name=
"main"),
428 Layout(size=10, name=
"footer"),
431 layout[
"main"].split_row(
Layout(name=
"side"),
Layout(name=
"body", ratio=2))
433 layout[
"body"].split_row(
Layout(name=
"content", ratio=2),
Layout(name=
"s2"))
435 layout[
"s2"].split_column(
441 layout[
"content"].update(
"foo")
Iterable[Tuple["Layout", Region]] divide(self, Sequence["Layout"] children, Region region)
None refresh_screen(self, "Console" console, str layout_name)
RenderMap render(self, Console console, ConsoleOptions options)
None add_split(self, *Union["Layout", RenderableType] layouts)
None split_row(self, *Union["Layout", RenderableType] layouts)
RenderableType renderable(self)
RenderResult __rich_console__(self, Console console, ConsoleOptions options)
"Layout" __getitem__(self, str name)
None split_column(self, *Union["Layout", RenderableType] layouts)
None __init__(self, Optional[RenderableType] renderable=None, *Optional[str] name=None, Optional[int] size=None, int minimum_size=1, int ratio=1, bool visible=True)
None update(self, RenderableType renderable)
None split(self, *Union["Layout", RenderableType] layouts, Union[Splitter, str] splitter="column")
Optional["Layout"] get(self, str name)
RegionMap _make_region_map(self, int width, int height)
Result __rich_repr__(self)
List["Layout"] children(self)
Iterable[Tuple["Layout", Region]] divide(self, Sequence["Layout"] children, Region region)
Iterable[Tuple["Layout", Region]] divide(self, Sequence["Layout"] children, Region region)
None __init__(self, "Layout" layout, StyleType style="")
RenderResult __rich_console__(self, Console console, ConsoleOptions options)