fix for spatial map and layout

This commit is contained in:
Will McGugan
2024-11-18 14:03:53 +00:00
parent 845840e830
commit 4c805bf442
8 changed files with 73 additions and 35 deletions

View File

@@ -580,15 +580,9 @@ class Compositor:
add_new_widget(widget)
else:
add_new_invisible_widget(widget)
styles_offset = styles.offset
layout_offset = (
styles_offset.resolve(region.size, clip.size)
if styles_offset
else ORIGIN
)
# Container region is minus border
container_region = region.shrink(styles.gutter).translate(layout_offset)
container_region = region.shrink(styles.gutter)
container_size = container_region.size
# Widgets with scrollbars (containers or scroll view) require additional processing
@@ -613,9 +607,12 @@ class Compositor:
sub_clip = clip.intersection(child_region)
if visible_only:
placements = arrange_result.get_visible_placements(
widget_window = (
sub_clip - child_region.offset + widget.scroll_offset
)
placements = arrange_result.get_visible_placements(
widget_window
)
else:
placements = arrange_result.placements
total_region = total_region.union(arrange_result.total_region)
@@ -643,15 +640,25 @@ class Compositor:
)
# Add all the widgets
for sub_region, _, _, sub_widget, z, fixed, overlay in reversed(
placements
):
for (
sub_region,
sub_region_offset,
_,
sub_widget,
z,
fixed,
overlay,
) in reversed(placements):
layer_index = get_layer_index(sub_widget.layer, 0)
# Combine regions with children to calculate the "virtual size"
if fixed:
widget_region = sub_region + placement_offset
widget_region = (
sub_region + sub_region_offset + placement_offset
)
else:
widget_region = sub_region + placement_scroll_offset
widget_region = (
sub_region + sub_region_offset + placement_scroll_offset
)
widget_order = order + ((layer_index, z, layer_order),)
@@ -699,7 +706,7 @@ class Compositor:
)
map[widget] = _MapGeometry(
region + layout_offset,
region,
order,
clip,
total_region.size,
@@ -711,7 +718,7 @@ class Compositor:
elif visible:
# Add the widget to the map
widget_region = region + layout_offset
widget_region = region
if widget.absolute_offset is not None:
margin = styles.margin

View File

@@ -6,7 +6,7 @@ from typing import Generic, Iterable, TypeVar
from typing_extensions import TypeAlias
from textual.geometry import Region
from textual.geometry import Offset, Region
ValueType = TypeVar("ValueType")
GridCoordinate: TypeAlias = "tuple[int, int]"
@@ -57,7 +57,7 @@ class SpatialMap(Generic[ValueType]):
)
def insert(
self, regions_and_values: Iterable[tuple[Region, bool, bool, ValueType]]
self, regions_and_values: Iterable[tuple[Region, Offset, bool, bool, ValueType]]
) -> None:
"""Insert values into the Spatial map.
@@ -65,19 +65,19 @@ class SpatialMap(Generic[ValueType]):
indicates fixed regions. Fixed regions don't scroll and are always visible.
Args:
regions_and_values: An iterable of (REGION, FIXED, OVERLAY, VALUE).
regions_and_values: An iterable of (REGION, OFFSET, FIXED, OVERLAY, VALUE).
"""
append_fixed = self._fixed.append
get_grid_list = self._map.__getitem__
_region_to_grid = self._region_to_grid_coordinates
total_region = self.total_region
for region, fixed, overlay, value in regions_and_values:
for region, offset, fixed, overlay, value in regions_and_values:
if fixed:
append_fixed(value)
else:
if not overlay:
total_region = total_region.union(region)
for grid in _region_to_grid(region):
for grid in _region_to_grid(region + offset):
get_grid_list(grid).append(value)
self.total_region = total_region

View File

@@ -39,6 +39,7 @@ class DockArrangeResult:
self._spatial_map.insert(
(
placement.region.grow(placement.margin),
placement.offset,
placement.fixed,
placement.overlay,
placement,
@@ -75,7 +76,7 @@ class DockArrangeResult:
culled_placements = [
placement
for placement in visible_placements
if placement.fixed or overlaps(placement.region)
if placement.fixed or overlaps(placement.region + placement.offset)
]
return culled_placements

View File

@@ -284,15 +284,23 @@ class GridLayout(Layout):
if (height > cell_size.height)
else Fraction(cell_size.height)
)
region = (
Region(x, y, int(width + margin.width), int(height + margin.height))
.crop_size(cell_size)
.shrink(margin)
)
offset = (
styles.offset.resolve(cell_size, viewport)
if styles.has_rule("offset")
else NULL_OFFSET
)
add_placement(
WidgetPlacement(
region + offset,
NULL_OFFSET,
region,
offset,
(
margin
if gutter_spacing is None

View File

@@ -24,6 +24,7 @@ class HorizontalLayout(Layout):
) -> ArrangeResult:
placements: list[WidgetPlacement] = []
add_placement = placements.append
viewport = parent.app.size
child_styles = [child.styles for child in children]
box_margins: list[Spacing] = [
@@ -52,7 +53,7 @@ class HorizontalLayout(Layout):
[styles.width for styles in child_styles],
children,
size,
parent.app.size,
viewport,
resolve_margin,
resolve_dimension="width",
)
@@ -75,10 +76,20 @@ class HorizontalLayout(Layout):
_Region = Region
_WidgetPlacement = WidgetPlacement
_Size = Size
for widget, (content_width, content_height, box_margin), margin in zip(
children, box_models, margins
):
overlay = widget.styles.overlay == "screen"
styles = widget.styles
overlay = styles.overlay == "screen"
offset = (
styles.offset.resolve(
_Size(content_width.__floor__(), content_height.__floor__()),
viewport,
)
if styles.has_rule("offset")
else NULL_OFFSET
)
offset_y = box_margin.top
next_x = x + content_width
add_placement(
@@ -89,7 +100,7 @@ class HorizontalLayout(Layout):
(next_x - x.__floor__()).__floor__(),
content_height.__floor__(),
),
NULL_OFFSET,
offset,
box_margin,
widget,
0,

View File

@@ -22,6 +22,7 @@ class VerticalLayout(Layout):
) -> ArrangeResult:
placements: list[WidgetPlacement] = []
add_placement = placements.append
viewport = parent.app.size
child_styles = [child.styles for child in children]
box_margins: list[Spacing] = [
@@ -80,11 +81,21 @@ class VerticalLayout(Layout):
_Region = Region
_WidgetPlacement = WidgetPlacement
_Size = Size
for widget, (content_width, content_height, box_margin), margin in zip(
children, box_models, margins
):
overlay = widget.styles.overlay == "screen"
styles = widget.styles
overlay = styles.overlay == "screen"
next_y = y + content_height
offset = (
styles.offset.resolve(
_Size(content_width.__floor__(), content_height.__floor__()),
viewport,
)
if styles.has_rule("offset")
else NULL_OFFSET
)
add_placement(
_WidgetPlacement(
_Region(
@@ -93,7 +104,7 @@ class VerticalLayout(Layout):
content_width.__floor__(),
next_y.__floor__() - y.__floor__(),
),
NULL_OFFSET,
offset,
box_margin,
widget,
0,

View File

@@ -437,9 +437,9 @@ class Widget(DOMNode):
self._content_width_cache: tuple[object, int] = (None, 0)
self._content_height_cache: tuple[object, int] = (None, 0)
self._arrangement_cache: FIFOCache[tuple[Size, int], DockArrangeResult] = (
FIFOCache(4)
)
self._arrangement_cache: FIFOCache[
tuple[Size, int, Widget], DockArrangeResult
] = FIFOCache(4)
self._styles_cache = StylesCache()
self._rich_style_cache: dict[tuple[str, ...], tuple[Style, Style]] = {}

View File

@@ -1,7 +1,7 @@
import pytest
from textual._spatial_map import SpatialMap
from textual.geometry import Region
from textual.geometry import Offset, Region
@pytest.mark.parametrize(
@@ -44,9 +44,9 @@ def test_get_values_in_region() -> None:
spatial_map.insert(
[
(Region(10, 5, 5, 5), False, False, "foo"),
(Region(5, 20, 5, 5), False, False, "bar"),
(Region(0, 0, 40, 1), True, False, "title"),
(Region(10, 5, 5, 5), Offset(), False, False, "foo"),
(Region(5, 20, 5, 5), Offset(), False, False, "bar"),
(Region(0, 0, 40, 1), Offset(), True, False, "title"),
]
)