integrated spatial map

This commit is contained in:
Will McGugan
2022-11-23 22:12:44 +08:00
parent 039e4ad4b6
commit f84313dac7
4 changed files with 31 additions and 20 deletions

View File

@@ -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

View File

@@ -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):

View File

@@ -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)

View File

@@ -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: