mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
faster layout in stream
This commit is contained in:
@@ -53,7 +53,6 @@ def arrange(
|
||||
Returns:
|
||||
Widget arrangement information.
|
||||
"""
|
||||
print("ARRANGE", widget)
|
||||
placements: list[WidgetPlacement] = []
|
||||
scroll_spacing = NULL_SPACING
|
||||
styles = widget.styles
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from itertools import zip_longest
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from textual.geometry import NULL_OFFSET, Region, Size
|
||||
@@ -30,6 +31,9 @@ class StreamLayout(Layout):
|
||||
|
||||
name = "stream"
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._cache: dict[object, list[WidgetPlacement]] = {}
|
||||
|
||||
def arrange(
|
||||
self, parent: Widget, children: list[Widget], size: Size, greedy: bool = True
|
||||
) -> ArrangeResult:
|
||||
@@ -38,6 +42,10 @@ class StreamLayout(Layout):
|
||||
return []
|
||||
viewport = parent.app.viewport_size
|
||||
|
||||
cache_key = (size, viewport)
|
||||
previous_results = self._cache.get(cache_key, None) or []
|
||||
layout_widgets = parent.screen._layout_widgets.get(parent, [])
|
||||
|
||||
_Region = Region
|
||||
_WidgetPlacement = WidgetPlacement
|
||||
|
||||
@@ -48,7 +56,18 @@ class StreamLayout(Layout):
|
||||
previous_margin = first_child_styles.margin.top
|
||||
null_offset = NULL_OFFSET
|
||||
|
||||
for widget in children:
|
||||
pre_populate = bool(previous_results and layout_widgets)
|
||||
for widget, placement in zip_longest(children, previous_results):
|
||||
if pre_populate and placement is not None and widget == placement.widget:
|
||||
if widget in layout_widgets:
|
||||
pre_populate = False
|
||||
else:
|
||||
placements.append(placement)
|
||||
y = placement.region.bottom
|
||||
continue
|
||||
if widget is None:
|
||||
break
|
||||
|
||||
styles = widget.styles._base_styles
|
||||
margin = styles.margin
|
||||
gutter_width, gutter_height = styles.gutter.totals
|
||||
@@ -85,6 +104,7 @@ class StreamLayout(Layout):
|
||||
)
|
||||
y += height
|
||||
|
||||
self._cache[cache_key] = placements
|
||||
return placements
|
||||
|
||||
def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> int:
|
||||
|
||||
@@ -11,19 +11,15 @@ if TYPE_CHECKING:
|
||||
from textual.geometry import Spacing
|
||||
from textual.widget import Widget
|
||||
|
||||
from textual._profile import timer
|
||||
|
||||
|
||||
class VerticalLayout(Layout):
|
||||
"""Used to layout Widgets vertically on screen, from top to bottom."""
|
||||
|
||||
name = "vertical"
|
||||
|
||||
@timer("Vertical.arrange")
|
||||
def arrange(
|
||||
self, parent: Widget, children: list[Widget], size: Size, greedy: bool = True
|
||||
) -> ArrangeResult:
|
||||
print(parent, size, greedy, len(children))
|
||||
parent.pre_layout(self)
|
||||
placements: list[WidgetPlacement] = []
|
||||
add_placement = placements.append
|
||||
|
||||
@@ -362,8 +362,6 @@ class Reactive(Generic[ReactiveType]):
|
||||
|
||||
# Refresh according to descriptor flags
|
||||
if self._layout or self._repaint or self._recompose:
|
||||
if self._layout:
|
||||
print(self, "layout", obj)
|
||||
obj.refresh(
|
||||
repaint=self._repaint,
|
||||
layout=self._layout,
|
||||
|
||||
@@ -321,7 +321,7 @@ class Screen(Generic[ScreenResultType], Widget):
|
||||
self._css_update_count = -1
|
||||
"""Track updates to CSS."""
|
||||
|
||||
self._layout_widgets: set[Widget] = set()
|
||||
self._layout_widgets: dict[DOMNode, set[Widget]] = {}
|
||||
"""Widgets whose layout may have changed."""
|
||||
|
||||
@property
|
||||
@@ -1353,8 +1353,22 @@ class Screen(Generic[ScreenResultType], Widget):
|
||||
async def _on_layout(self, message: messages.Layout) -> None:
|
||||
message.stop()
|
||||
message.prevent_default()
|
||||
if message.widget not in self._layout_widgets:
|
||||
self._layout_widgets.add(message.widget)
|
||||
|
||||
layout_required = True
|
||||
widget: DOMNode = message.widget
|
||||
for ancestor in message.widget.ancestors:
|
||||
if not isinstance(ancestor, Widget):
|
||||
break
|
||||
if ancestor not in self._layout_widgets:
|
||||
self._layout_widgets[ancestor] = set()
|
||||
# assert isinstance(widget, Widget)
|
||||
self._layout_widgets[ancestor].add(widget)
|
||||
layout_required = True
|
||||
if not ancestor.styles.auto_dimensions:
|
||||
break
|
||||
widget = ancestor
|
||||
|
||||
if layout_required:
|
||||
self._layout_required = True
|
||||
self.check_idle()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user