mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
fix for spatial map and layout
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]] = {}
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user