diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index c4e240e2e9..945d0841d4 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -614,9 +614,8 @@ def add_widget( - widget.scrollbar_size_horizontal ) ) - capped_scroll_y = widget.validate_scroll_y(new_scroll_y) - widget.set_reactive(Widget.scroll_y, capped_scroll_y) - widget.set_reactive(Widget.scroll_target_y, capped_scroll_y) + widget.set_reactive(Widget.scroll_y, new_scroll_y) + widget.set_reactive(Widget.scroll_target_y, new_scroll_y) widget.vertical_scrollbar._reactive_position = new_scroll_y if visible_only: diff --git a/src/textual/widget.py b/src/textual/widget.py index f0b064fb27..4d6115ff97 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -4274,9 +4274,7 @@ def remove_children( ] else: children_to_remove = selector - await_remove = self.app._prune( - *children_to_remove, parent=cast(DOMNode, self._parent) - ) + await_remove = self.app._prune(*children_to_remove, parent=self) return await_remove @asynccontextmanager @@ -4423,11 +4421,12 @@ def _check_refresh(self) -> None: self.call_later(self._update_styles) if self._scroll_required: self._scroll_required = False - if self.styles.keyline[0] != "none": - # TODO: Feels like a hack - # Perhaps there should be an explicit mechanism for backgrounds to refresh when scrolled? - self._set_dirty() - screen.post_message(messages.UpdateScroll()) + if not self._layout_required: + if self.styles.keyline[0] != "none": + # TODO: Feels like a hack + # Perhaps there should be an explicit mechanism for backgrounds to refresh when scrolled? + self._set_dirty() + screen.post_message(messages.UpdateScroll()) if self._repaint_required: self._repaint_required = False if self.display: diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_prune_fix.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_prune_fix.svg new file mode 100644 index 0000000000..d184e8372c --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_prune_fix.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PruneApp + + + + + + + + + + Hello +World + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 1ace31a82c..0f99dbaf51 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -4713,3 +4713,29 @@ def compose(self) -> ComposeResult: yield Static("Hello, World! 293487 " * 200) assert snap_compare(ScrollbarApp()) + + +def test_prune_fix(snap_compare) -> None: + """Regression test for https://github.com/Textualize/textual/issues/6205 + + You should see the text "Hello" and "World" across the first two lines. + The original issue is that a layout operation wasn't done after removing children, leaving + a large gap between "Hello" and "World" + + """ + + class PruneApp(App): + BINDINGS = [Binding("c", "clear", priority=True)] + + def compose(self) -> ComposeResult: + yield Static("Hello") + with VerticalGroup(): + for i in range(10): + yield Static(str(i)) + yield Static("World") + + async def action_clear(self) -> None: + vs = self.query_one(VerticalGroup) + await vs.remove_children() + + assert snap_compare(PruneApp(), press=["c"])