diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cc55decb2..03f7142acd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed zero division error https://github.com/Textualize/textual/issues/2673 - Fix `scroll_to_center` when there were nested layers out of view (Compositor full_map not populated fully) https://github.com/Textualize/textual/pull/2684 +- Issues with `switch_screen` not updating the results callback appropriately https://github.com/Textualize/textual/issues/2650 ### Added @@ -35,6 +36,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `Tree` and `DirectoryTree` Messages no longer accept a `tree` parameter, using `self.node.tree` instead. https://github.com/Textualize/textual/issues/2529 - Keybinding right in `Input` is also used to accept a suggestion if the cursor is at the end of the input https://github.com/Textualize/textual/pull/2604 - `Input.__init__` now accepts a `suggester` attribute for completion suggestions https://github.com/Textualize/textual/pull/2604 +- Using `switch_screen` to switch to the currently active screen is now a no-op https://github.com/Textualize/textual/pull/2692 ### Removed diff --git a/src/textual/app.py b/src/textual/app.py index 879edbad5b..e9300f8c82 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -1611,15 +1611,19 @@ def switch_screen(self, screen: Screen | str) -> AwaitMount: raise TypeError( f"switch_screen requires a Screen instance or str; not {screen!r}" ) - if self.screen is not screen: - previous_screen = self._replace_screen(self._screen_stack.pop()) - previous_screen._pop_result_callback() - next_screen, await_mount = self._get_screen(screen) - self._screen_stack.append(next_screen) - self.screen.post_message(events.ScreenResume()) - self.log.system(f"{self.screen} is current (SWITCHED)") - return await_mount - return AwaitMount(self.screen, []) + + next_screen, await_mount = self._get_screen(screen) + if screen is self.screen or next_screen is self.screen: + self.log.system(f"Screen {screen} is already current.") + return AwaitMount(self.screen, []) + + previous_screen = self._replace_screen(self._screen_stack.pop()) + previous_screen._pop_result_callback() + self._screen_stack.append(next_screen) + self.screen.post_message(events.ScreenResume()) + self.screen._push_result_callback(self.screen, None) + self.log.system(f"{self.screen} is current (SWITCHED)") + return await_mount def install_screen(self, screen: Screen, name: str) -> None: """Install a screen. diff --git a/tests/test_screens.py b/tests/test_screens.py index 033891990a..7f42a21a2e 100644 --- a/tests/test_screens.py +++ b/tests/test_screens.py @@ -298,3 +298,55 @@ async def key_p(self) -> None: await pilot.press("p") with pytest.raises(ScreenStackError): app.bottom.dismiss() + + +async def test_switch_screen_no_op(): + """Regression test for https://github.com/Textualize/textual/issues/2650""" + + class MyScreen(Screen): + pass + + class MyApp(App[None]): + SCREENS = {"screen": MyScreen()} + + def on_mount(self): + self.push_screen("screen") + + app = MyApp() + async with app.run_test(): + screen_id = id(app.screen) + app.switch_screen("screen") + assert screen_id == id(app.screen) + app.switch_screen("screen") + assert screen_id == id(app.screen) + + +async def test_switch_screen_updates_results_callback_stack(): + """Regression test for https://github.com/Textualize/textual/issues/2650""" + + class ScreenA(Screen): + pass + + class ScreenB(Screen): + pass + + class MyApp(App[None]): + SCREENS = { + "a": ScreenA(), + "b": ScreenB(), + } + + def callback(self, _): + return 42 + + def on_mount(self): + self.push_screen("a", self.callback) + + app = MyApp() + async with app.run_test(): + assert len(app.screen._result_callbacks) == 1 + assert app.screen._result_callbacks[-1].callback(None) == 42 + + app.switch_screen("b") + assert len(app.screen._result_callbacks) == 1 + assert app.screen._result_callbacks[-1].callback is None