diff --git a/CHANGELOG.md b/CHANGELOG.md index 89140e36d..013ff9916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Type selectors can now contain numbers https://github.com/Textualize/textual/issues/1253 +- Fixed visibility not affecting children https://github.com/Textualize/textual/issues/1313 ## [0.5.0] - 2022-11-20 diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index ab9d69eca..b59710218 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -347,6 +347,7 @@ class Compositor: order: tuple[tuple[int, ...], ...], layer_order: int, clip: Region, + visible: bool, ) -> None: """Called recursively to place a widget and its children in the map. @@ -356,7 +357,12 @@ class Compositor: order (tuple[int, ...]): A tuple of ints to define the order. clip (Region): The clipping region (i.e. the viewport which contains it). """ - widgets.add(widget) + visibility = widget.styles.get_rule("visibility") + if visibility is not None: + visible = visibility == "visible" + + if visible: + widgets.add(widget) styles_offset = widget.styles.offset layout_offset = ( styles_offset.resolve(region.size, clip.size) @@ -420,32 +426,34 @@ class Compositor: widget_order, layer_order, sub_clip, + visible, ) layer_order -= 1 - # Add any scrollbars - for chrome_widget, chrome_region in widget._arrange_scrollbars( - container_region - ): - map[chrome_widget] = MapGeometry( - chrome_region + layout_offset, + if visible: + # Add any scrollbars + for chrome_widget, chrome_region in widget._arrange_scrollbars( + container_region + ): + map[chrome_widget] = MapGeometry( + chrome_region + layout_offset, + order, + clip, + container_size, + container_size, + chrome_region, + ) + + map[widget] = MapGeometry( + region + layout_offset, order, clip, + total_region.size, container_size, - container_size, - chrome_region, + virtual_region, ) - map[widget] = MapGeometry( - region + layout_offset, - order, - clip, - total_region.size, - container_size, - virtual_region, - ) - - else: + elif visible: # Add the widget to the map map[widget] = MapGeometry( region + layout_offset, @@ -457,7 +465,15 @@ class Compositor: ) # Add top level (root) widget - add_widget(root, size.region, size.region, ((0,),), layer_order, size.region) + add_widget( + root, + size.region, + size.region, + ((0,),), + layer_order, + size.region, + True, + ) return map, widgets @property @@ -630,11 +646,6 @@ class Compositor: if not self.map: return - def is_visible(widget: Widget) -> bool: - """Return True if the widget is (literally) visible by examining various - properties which affect whether it can be seen or not.""" - return widget.visible and widget.styles.opacity > 0 - _Region = Region visible_widgets = self.visible_widgets @@ -644,13 +655,13 @@ class Compositor: widget_regions = [ (widget, region, clip) for widget, (region, clip) in visible_widgets.items() - if crop_overlaps(clip) and is_visible(widget) + if crop_overlaps(clip) and widget.styles.opacity > 0 ] else: widget_regions = [ (widget, region, clip) for widget, (region, clip) in visible_widgets.items() - if is_visible(widget) + if widget.styles.opacity > 0 ] intersection = _Region.intersection diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index c2ae2ab5f..53db5e9ec 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -7422,3 +7422,160 @@ ''' # --- +# name: test_visibility + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Visibility + + + + + + + + + + ┌──────────────────────────────────────┐ + bar + ┌────────────────────────────────────┐┌────────────────────────────────────┐ + floatfloat + └────────────────────────────────────┘└────────────────────────────────────┘ + + + + + + + + + + + + + + + + + + + └──────────────────────────────────────┘ + + + + + ''' +# --- diff --git a/tests/snapshot_tests/snapshot_apps/vis.html b/tests/snapshot_tests/snapshot_apps/vis.html new file mode 100644 index 000000000..4e4ac19e1 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/vis.html @@ -0,0 +1,34 @@ + + + + + + + + +
+
+
+ Hello, World + +
+ +
+ + +
+ + + + diff --git a/tests/snapshot_tests/snapshot_apps/visibility.py b/tests/snapshot_tests/snapshot_apps/visibility.py new file mode 100644 index 000000000..a5ccccb7e --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/visibility.py @@ -0,0 +1,48 @@ +from textual.app import App +from textual.containers import Vertical +from textual.widgets import Static + + +class Visibility(App): + """Check that visibility: hidden also makes children invisible;""" + + CSS = """ + Screen { + layout: horizontal; + } + Vertical { + width: 1fr; + border: solid red; + } + + #container1 { + visibility: hidden; + } + + .float { + border: solid blue; + } + + /* Make a child of a hidden widget visible again */ + #container1 .float { + visibility: visible; + } + """ + + def compose(self): + + yield Vertical( + Static("foo"), + Static("float", classes="float"), + id="container1", + ) + yield Vertical( + Static("bar"), + Static("float", classes="float"), + id="container2", + ) + + +if __name__ == "__main__": + app = Visibility() + app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 338d3fb5d..7efcd3b2a 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -100,6 +100,10 @@ def test_fr_units(snap_compare): assert snap_compare("snapshot_apps/fr_units.py") +def test_visibility(snap_compare): + assert snap_compare("snapshot_apps/visibility.py") + + def test_tree_example(snap_compare): assert snap_compare(WIDGET_EXAMPLES_DIR / "tree.py")