mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
integrated spatial map
This commit is contained in:
@@ -381,9 +381,14 @@ class Compositor:
|
||||
|
||||
if widget.is_container:
|
||||
# Arrange the layout
|
||||
placements, arranged_widgets, spacing = widget._arrange(
|
||||
spatial_map, arranged_widgets, spacing = widget._arrange(
|
||||
child_region.size
|
||||
)
|
||||
|
||||
placements = spatial_map.get_placements(
|
||||
child_region.reset_offset.translate(widget.scroll_offset)
|
||||
)
|
||||
|
||||
widgets.update(arranged_widgets)
|
||||
|
||||
# An offset added to all placements
|
||||
|
||||
@@ -8,9 +8,10 @@ from ._typing import TypeAlias
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .widget import Widget
|
||||
from ._spatial_map import SpatialMap
|
||||
|
||||
ArrangeResult: TypeAlias = "tuple[list[WidgetPlacement], set[Widget]]"
|
||||
DockArrangeResult: TypeAlias = "tuple[list[WidgetPlacement], set[Widget], Spacing]"
|
||||
DockArrangeResult: TypeAlias = "SpacialMap, set[Widget], Spacing]"
|
||||
|
||||
|
||||
class WidgetPlacement(NamedTuple):
|
||||
|
||||
@@ -2,10 +2,12 @@ from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
from itertools import product
|
||||
from typing import Iterable, Mapping
|
||||
from operator import attrgetter
|
||||
from typing import Iterable, Mapping, Sequence
|
||||
|
||||
from ._layout import WidgetPlacement
|
||||
from .geometry import Region
|
||||
from ._partition import partition
|
||||
|
||||
|
||||
class SpatialMap:
|
||||
@@ -23,26 +25,23 @@ class SpatialMap:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
placements: Iterable[WidgetPlacement],
|
||||
placements: Sequence[WidgetPlacement],
|
||||
block_width: int = 80,
|
||||
block_height: int = 80,
|
||||
) -> None:
|
||||
self._placements = placements
|
||||
self._block_width = block_width
|
||||
self._block_height = block_height
|
||||
self._fixed: list[WidgetPlacement] = []
|
||||
self._map: defaultdict[tuple[int, int], list[WidgetPlacement]] | None = None
|
||||
|
||||
@property
|
||||
def placement_map(self) -> Mapping[tuple[int, int], list[WidgetPlacement]]:
|
||||
"""A mapping of block coordinate on to widget placement.
|
||||
self.placement_map = self._build_placements(placements)
|
||||
|
||||
Returns:
|
||||
Mapping[tuple[int, int], list[WidgetPlacement]]: Mapping of coord to list of placements.
|
||||
"""
|
||||
if self._map is None:
|
||||
self._map = self._build_placements(self._placements)
|
||||
return self._map
|
||||
return self._map
|
||||
def __iter__(self) -> Iterable[WidgetPlacement]:
|
||||
yield from self._placements
|
||||
|
||||
def __reversed__(self) -> Iterable[WidgetPlacement]:
|
||||
yield from reversed(self._placements)
|
||||
|
||||
def _build_placements(
|
||||
self, placements: Iterable[WidgetPlacement]
|
||||
@@ -58,6 +57,8 @@ class SpatialMap:
|
||||
block_width = self._block_width
|
||||
block_height = self._block_height
|
||||
|
||||
placements, self._fixed = partition(attrgetter("fixed"), placements)
|
||||
|
||||
for placement in placements:
|
||||
x1, y1, width, height = placement.region
|
||||
x2 = x1 + width
|
||||
@@ -69,7 +70,7 @@ class SpatialMap:
|
||||
get_bucket(coord).append(placement)
|
||||
return map
|
||||
|
||||
def get_placements(self, screen_region: Region) -> Iterable[WidgetPlacement]:
|
||||
def get_placements(self, screen_region: Region) -> list[WidgetPlacement]:
|
||||
"""Get placements that may overlap a given region. There may be false positives,
|
||||
but no false negatives.
|
||||
|
||||
@@ -85,7 +86,7 @@ class SpatialMap:
|
||||
block_width = self._block_width
|
||||
block_height = self._block_height
|
||||
|
||||
placements: set[WidgetPlacement] = set()
|
||||
placements: set[WidgetPlacement] = set(self._fixed)
|
||||
extend_placements = placements.update
|
||||
map = self.placement_map
|
||||
map_get = map.get
|
||||
@@ -98,4 +99,4 @@ class SpatialMap:
|
||||
if block_placements is not None:
|
||||
extend_placements(block_placements)
|
||||
|
||||
return placements
|
||||
return list(placements)
|
||||
@@ -1,8 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import Counter
|
||||
from asyncio import Event as AsyncEvent
|
||||
from asyncio import Lock, create_task, wait
|
||||
from collections import Counter
|
||||
from fractions import Fraction
|
||||
from itertools import islice
|
||||
from operator import attrgetter
|
||||
@@ -39,6 +39,7 @@ from ._context import active_app
|
||||
from ._easing import DEFAULT_SCROLL_EASING
|
||||
from ._layout import Layout
|
||||
from ._segment_tools import align_lines
|
||||
from ._spatial_map import SpatialMap
|
||||
from ._styles_cache import StylesCache
|
||||
from ._types import Lines
|
||||
from .await_remove import AwaitRemove
|
||||
@@ -53,7 +54,6 @@ from .message import Message
|
||||
from .messages import CallbackType
|
||||
from .reactive import Reactive
|
||||
from .render import measure
|
||||
from .await_remove import AwaitRemove
|
||||
from .walk import walk_depth_first
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -426,7 +426,11 @@ class Widget(DOMNode):
|
||||
return self._arrangement
|
||||
|
||||
self._arrangement_cache_key = arrange_cache_key
|
||||
self._arrangement = arrange(self, self.children, size, self.screen.size)
|
||||
placements, widgets, spacing = arrange(
|
||||
self, self.children, size, self.screen.size
|
||||
)
|
||||
arrange_result = SpatialMap(placements), widgets, spacing
|
||||
self._arrangement = arrange_result
|
||||
return self._arrangement
|
||||
|
||||
def _clear_arrangement_cache(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user