diff --git a/changes/4631.bugfix.md b/changes/4631.bugfix.md new file mode 100644 index 0000000000..0910c73a40 --- /dev/null +++ b/changes/4631.bugfix.md @@ -0,0 +1 @@ +A failed `SplitContainer.content` assignment no longer orphans the currently-displayed widgets. diff --git a/core/src/toga/widgets/splitcontainer.py b/core/src/toga/widgets/splitcontainer.py index 069a9bdde8..54de69368b 100644 --- a/core/src/toga/widgets/splitcontainer.py +++ b/core/src/toga/widgets/splitcontainer.py @@ -97,11 +97,6 @@ def content(self) -> list[SplitContainerContentT]: @content.setter def content(self, content: Sequence[SplitContainerContentT]) -> None: - for old_content in self._content: - if old_content is not None: - old_content.app = None - old_content.window = None - try: if len(content) != 2: raise TypeError() @@ -131,6 +126,14 @@ def content(self, content: Sequence[SplitContainerContentT]) -> None: _content.append(widget) flex.append(flex_value) + # Only detach the old content once the new content is fully validated, + # so a failed assignment doesn't orphan the widgets currently shown. + for old_content in self._content: + if old_content is not None: + old_content.app = None + old_content.window = None + + for widget in _content: if widget: widget.app = self.app widget.window = self.window diff --git a/core/tests/widgets/test_splitcontainer.py b/core/tests/widgets/test_splitcontainer.py index 8bfadf5edb..c43010ab5a 100644 --- a/core/tests/widgets/test_splitcontainer.py +++ b/core/tests/widgets/test_splitcontainer.py @@ -412,6 +412,23 @@ def test_set_content_invalid(splitcontainer, content, message): splitcontainer.content = content +def test_set_content_invalid_keeps_previous_content(content1, content2): + """A failed content assignment must not orphan the widgets currently shown.""" + app = toga.App("Test App", "org.beeware.toga.splitcontainer-test") + window = toga.Window() + splitcontainer = toga.SplitContainer(content=[content1, content2]) + window.content = splitcontainer + + assert content1.id in app.widgets + + with pytest.raises(ValueError): + splitcontainer.content = [content1] + + # The previous content is still attached and intact + assert content1.id in app.widgets + assert splitcontainer.content == [content1, content2] + + def test_direction(splitcontainer): """The direction of the splitcontainer can be changed."""