Stream layout

This commit is contained in:
Will McGugan
2025-08-01 09:29:21 +01:00
parent 7c2b65f6fd
commit 925c520318
5 changed files with 63 additions and 2 deletions

1
.gitignore vendored
View File

@@ -29,6 +29,7 @@ lib/
lib64/
parts/
sdist/
dist/
var/
wheels/
*.egg-info/

View File

@@ -28,7 +28,7 @@ VALID_BORDER: Final = {
"wide",
}
VALID_EDGE: Final = {"top", "right", "bottom", "left", "none"}
VALID_LAYOUT: Final = {"vertical", "horizontal", "grid"}
VALID_LAYOUT: Final = {"vertical", "horizontal", "grid", "stream"}
VALID_BOX_SIZING: Final = {"border-box", "content-box"}
VALID_OVERFLOW: Final = {"scroll", "hidden", "auto"}

View File

@@ -284,7 +284,7 @@ class StylesBase:
layout = LayoutProperty()
"""Set the layout of the widget, defining how it's children are laid out.
Valid values are "grid", "horizontal", and "vertical" or None to clear any layout
Valid values are "grid", "stream", "horizontal", or "vertical" or None to clear any layout
that was set at runtime.
Raises:

View File

@@ -3,12 +3,14 @@ from __future__ import annotations
from textual.layout import Layout
from textual.layouts.grid import GridLayout
from textual.layouts.horizontal import HorizontalLayout
from textual.layouts.stream import StreamLayout
from textual.layouts.vertical import VerticalLayout
LAYOUT_MAP: dict[str, type[Layout]] = {
"horizontal": HorizontalLayout,
"grid": GridLayout,
"vertical": VerticalLayout,
"stream": StreamLayout,
}

View File

@@ -0,0 +1,58 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from textual.geometry import NULL_OFFSET, Region, Size
from textual.layout import ArrangeResult, Layout, WidgetPlacement
if TYPE_CHECKING:
from textual.widget import Widget
class StreamLayout(Layout):
name = "stream"
def arrange(
self, parent: Widget, children: list[Widget], size: Size, greedy: bool = True
) -> ArrangeResult:
parent.pre_layout(self)
viewport = parent.app.size
placements: list[WidgetPlacement] = []
if not children:
return []
width = size.width
first_child_styles = children[0].styles
y = first_child_styles.margin.top
previous_margin = 0
null_offset = NULL_OFFSET
for widget in children:
styles = widget.styles
overlay = styles.overlay == "screen"
absolute = styles.has_rule("position") and styles.position == "absolute"
margin = styles.margin
top, right, bottom, left = margin
margin_width = left + right
y += max(top, previous_margin)
previous_margin = bottom
height = widget.get_content_height(size, viewport, width - margin_width)
height += styles.gutter.height
if (max_height := styles.max_height) is not None and max_height.is_cells:
height = min(height, int(max_height.value))
placements.append(
WidgetPlacement(
Region(left, y, width - margin_width, height),
null_offset,
margin,
widget,
0,
False,
overlay,
absolute,
)
)
y += height
return placements