From 82d6e3e05f87939166364e805f35e8260f395c9d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 7 Dec 2023 18:40:48 +0000 Subject: [PATCH] Roll back ALLOW_CHILDREN and max height fix (#3814) * max height * changelog * snapshot * unused exception --- CHANGELOG.md | 5 + src/textual/_layout.py | 12 +- src/textual/scroll_view.py | 2 - src/textual/widget.py | 9 - src/textual/widgets/_button.py | 2 - src/textual/widgets/_static.py | 2 - src/textual/widgets/_toggle_button.py | 2 - .../__snapshots__/test_snapshots.ambr | 158 ++++++++++++++++++ .../snapshot_apps/max_height_100.py | 27 +++ tests/snapshot_tests/test_snapshots.py | 5 + tests/test_widget.py | 20 +-- 11 files changed, 208 insertions(+), 36 deletions(-) create mode 100644 tests/snapshot_tests/snapshot_apps/max_height_100.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ddd8fb60dd..ed5eb24d0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Removed renderables/align.py which was no longer used +### Changed + +- Dropped ALLOW_CHILDREN flag introduced in 0.43.0 https://github.com/Textualize/textual/pull/3814 +- Widgets with an auto height in an auto height container will now expand if they have no siblings https://github.com/Textualize/textual/pull/3814 + ### Added - Added `get_loading_widget` to Widget and App customize the loading widget. https://github.com/Textualize/textual/pull/3816 diff --git a/src/textual/_layout.py b/src/textual/_layout.py index 03f1137c11..c147a0e419 100644 --- a/src/textual/_layout.py +++ b/src/textual/_layout.py @@ -183,7 +183,17 @@ def get_content_height( height = 0 else: # Use a height of zero to ignore relative heights - arrangement = widget._arrange(Size(width, 0)) + styles_height = widget.styles.height + if widget._parent and len(widget._nodes) == 1: + # If it is an only child with height auto we want it to expand + height = ( + container.height + if styles_height is not None and styles_height.is_auto + else 0 + ) + else: + height = 0 + arrangement = widget._arrange(Size(width, height)) height = arrangement.total_region.bottom return height diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index f62b8345d1..a4e3aa03d8 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -18,8 +18,6 @@ class ScrollView(ScrollableContainer): on the compositor to render children). """ - ALLOW_CHILDREN = False - DEFAULT_CSS = """ ScrollView { overflow-y: auto; diff --git a/src/textual/widget.py b/src/textual/widget.py index 56f77bcd0f..3ce45f72b4 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -86,10 +86,6 @@ } -class NotAContainer(Exception): - """Exception raised if you attempt to add a child to a widget which doesn't permit child nodes.""" - - _NULL_STYLE = Style() @@ -267,9 +263,6 @@ class Widget(DOMNode): BORDER_SUBTITLE: ClassVar[str] = "" """Initial value for border_subtitle attribute.""" - ALLOW_CHILDREN: ClassVar[bool] = True - """Set to `False` to prevent adding children to this widget.""" - can_focus: bool = False """Widget may receive focus.""" can_focus_children: bool = True @@ -518,8 +511,6 @@ def compose_add_child(self, widget: Widget) -> None: widget: A Widget to add. """ _rich_traceback_omit = True - if not self.ALLOW_CHILDREN: - raise NotAContainer(f"Can't add children to {type(widget)} widgets") self._nodes._append(widget) def __enter__(self) -> Self: diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index e5a2c2d8b0..e1163a4737 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -153,8 +153,6 @@ class Button(Widget, can_focus=True): BINDINGS = [Binding("enter", "press", "Press Button", show=False)] - ALLOW_CHILDREN = False - label: reactive[TextType] = reactive[TextType]("") """The text label that appears within the button.""" diff --git a/src/textual/widgets/_static.py b/src/textual/widgets/_static.py index 7e72e4bf3f..a9f485345b 100644 --- a/src/textual/widgets/_static.py +++ b/src/textual/widgets/_static.py @@ -44,8 +44,6 @@ class Static(Widget, inherit_bindings=False): } """ - ALLOW_CHILDREN = False - _renderable: RenderableType def __init__( diff --git a/src/textual/widgets/_toggle_button.py b/src/textual/widgets/_toggle_button.py index c8a6570b1b..93059f610b 100644 --- a/src/textual/widgets/_toggle_button.py +++ b/src/textual/widgets/_toggle_button.py @@ -51,8 +51,6 @@ class ToggleButton(Static, can_focus=True): | `toggle--label` | Targets the text label of the toggle button. | """ - ALLOW_CHILDREN = False - DEFAULT_CSS = """ ToggleButton { width: auto; diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 629dea2542..fba99a5e54 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -22022,6 +22022,164 @@ ''' # --- +# name: test_max_height_100 + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HappyDataTableFunApp + + + + + + + + + +  Column 0  Column 1  Column 2  Column 3  Column 4  Column 5  Column 6  Column  +  0         0         0         0         0         0         0         0       +  0         1         2         3         4         5         6         7       +  0         2         4         6         8         10        12        14      +  0         3         6         9         12        15        18        21      +  0         4         8         12        16        20        24        28     ▆▆ +  0         5         10        15        20        25        30        35      +  0         6         12        18        24        30        36        42      +  0         7         14        21        28        35        42        49      +  0         8         16        24        32        40        48        56      +  0         9         18        27        36        45        54        63      +  0         10        20        30        40        50        60        70      +  0         11        22        33        44        55        66        77      +  0         12        24        36        48        60        72        84      +  0         13        26        39        52        65        78        91      +  0         14        28        42        56        70        84        98      +  0         15        30        45        60        75        90        105     +  0         16        32        48        64        80        96        112     +  0         17        34        51        68        85        102       119     +  0         18        36        54        72        90        108       126     +  0         19        38        57        76        95        114       133     +  0         20        40        60        80        100       120       140     +  0         21        42        63        84        105       126       147     + + + + + + ''' +# --- # name: test_missing_vertical_scroll ''' diff --git a/tests/snapshot_tests/snapshot_apps/max_height_100.py b/tests/snapshot_tests/snapshot_apps/max_height_100.py new file mode 100644 index 0000000000..83bc94386a --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/max_height_100.py @@ -0,0 +1,27 @@ +from textual.app import App, ComposeResult +from textual.widgets import DataTable, Static + + +class HappyDataTableFunApp(App[None]): + """The DataTable should expand as if it has height 1fr.""" + + CSS = """ + DataTable { + max-height: 100%; + } + """ + + def populate(self, table: DataTable) -> DataTable: + for n in range(20): + table.add_column(f"Column {n}") + for row in range(100): + table.add_row(*[str(row * n) for n in range(20)]) + return table + + def compose(self) -> ComposeResult: + with Static(id="s"): + yield self.populate(DataTable()) + + +if __name__ == "__main__": + HappyDataTableFunApp().run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index ac9bcac3cd..bce1c2c121 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -937,6 +937,10 @@ def test_vertical_max_height(snap_compare): assert snap_compare(SNAPSHOT_APPS_DIR / "vertical_max_height.py") +def test_max_height_100(snap_compare): + """Test vertical max height takes border in to account.""" + assert snap_compare(SNAPSHOT_APPS_DIR / "max_height_100.py") + def test_loading_indicator(snap_compare): """Test loading indicator.""" # https://github.com/Textualize/textual/pull/3816 @@ -949,3 +953,4 @@ def test_loading_indicator_disables_widget(snap_compare): assert snap_compare( SNAPSHOT_APPS_DIR / "loading.py", press=["space", "down", "down", "space"] ) + diff --git a/tests/test_widget.py b/tests/test_widget.py index 62172220c1..44dfcde739 100644 --- a/tests/test_widget.py +++ b/tests/test_widget.py @@ -9,8 +9,8 @@ from textual.css.query import NoMatches from textual.geometry import Offset, Size from textual.message import Message -from textual.widget import MountError, NotAContainer, PseudoClasses, Widget -from textual.widgets import Label, LoadingIndicator, Static +from textual.widget import MountError, PseudoClasses, Widget +from textual.widgets import Label, LoadingIndicator @pytest.mark.parametrize( @@ -396,22 +396,6 @@ class TestWidgetIsMountedApp(App): assert widget.is_mounted is True -async def test_not_allow_children(): - """Regression test for https://github.com/Textualize/textual/pull/3758""" - - class TestAppExpectFail(App): - def compose(self) -> ComposeResult: - # Statics don't have children, so this should error - with Static(): - yield Label("foo") - - app = TestAppExpectFail() - - with pytest.raises(NotAContainer): - async with app.run_test(): - pass - - async def test_mount_error_not_widget(): class NotWidgetApp(App): def compose(self) -> ComposeResult: