11from __future__ import annotations
22
3+ from itertools import zip_longest
34from typing import TYPE_CHECKING
45
56from textual .geometry import NULL_OFFSET , Region , Size
@@ -30,6 +31,9 @@ class StreamLayout(Layout):
3031
3132 name = "stream"
3233
34+ def __init__ (self ) -> None :
35+ self ._cache : dict [object , list [WidgetPlacement ]] = {}
36+
3337 def arrange (
3438 self , parent : Widget , children : list [Widget ], size : Size , greedy : bool = True
3539 ) -> ArrangeResult :
@@ -38,6 +42,10 @@ def arrange(
3842 return []
3943 viewport = parent .app .viewport_size
4044
45+ cache_key = (size , viewport )
46+ previous_results = self ._cache .get (cache_key , None ) or []
47+ layout_widgets = parent .screen ._layout_widgets .get (parent , [])
48+
4149 _Region = Region
4250 _WidgetPlacement = WidgetPlacement
4351
@@ -48,7 +56,18 @@ def arrange(
4856 previous_margin = first_child_styles .margin .top
4957 null_offset = NULL_OFFSET
5058
51- for widget in children :
59+ pre_populate = bool (previous_results and layout_widgets )
60+ for widget , placement in zip_longest (children , previous_results ):
61+ if pre_populate and placement is not None and widget == placement .widget :
62+ if widget in layout_widgets :
63+ pre_populate = False
64+ else :
65+ placements .append (placement )
66+ y = placement .region .bottom
67+ continue
68+ if widget is None :
69+ break
70+
5271 styles = widget .styles ._base_styles
5372 margin = styles .margin
5473 gutter_width , gutter_height = styles .gutter .totals
@@ -85,6 +104,7 @@ def arrange(
85104 )
86105 y += height
87106
107+ self ._cache [cache_key ] = placements
88108 return placements
89109
90110 def get_content_width (self , widget : Widget , container : Size , viewport : Size ) -> int :
0 commit comments