From f420b44b8e39b5d56ec24de0c77099681cd8ff66 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 18 Apr 2023 15:48:46 +0100 Subject: [PATCH] Revert "Prevent reactive-watcher loop in Tabs / TabbedContent. (#2305)" This reverts commit 66a644845b8376bb041b78d82756d7fa7b7b5d49. --- CHANGELOG.md | 1 - src/textual/widgets/_tabbed_content.py | 27 ++- .../__snapshots__/test_snapshots.ambr | 159 ------------------ .../snapshot_apps/quickly_change_tabs.py | 24 --- tests/snapshot_tests/test_snapshots.py | 5 - 5 files changed, 20 insertions(+), 196 deletions(-) delete mode 100644 tests/snapshot_tests/snapshot_apps/quickly_change_tabs.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 098fd57ed4..ee2e9d5586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fix empty ListView preventing bindings from firing https://github.com/Textualize/textual/pull/2281 - Fix `get_component_styles` returning incorrect values on first call when combined with pseudoclasses https://github.com/Textualize/textual/pull/2304 - Fixed `active_message_pump.get` sometimes resulting in a `LookupError` https://github.com/Textualize/textual/issues/2301 -- Fixed issue arising when active tab was changed too quickly in succession https://github.com/Textualize/textual/pull/2305 ## [0.19.1] - 2023-04-10 diff --git a/src/textual/widgets/_tabbed_content.py b/src/textual/widgets/_tabbed_content.py index 3792ed5336..ffaa1112b9 100644 --- a/src/textual/widgets/_tabbed_content.py +++ b/src/textual/widgets/_tabbed_content.py @@ -83,6 +83,9 @@ class TabbedContent(Widget): } """ + active: reactive[str] = reactive("", init=False) + """The ID of the active tab, or empty string if none are active.""" + class TabActivated(Message): """Posted when the active tab changes.""" @@ -113,16 +116,21 @@ def __init__(self, *titles: TextType, initial: str = "") -> None: self._initial = initial super().__init__() - @property - def active(self) -> str: - """The ID of the active tab, or empty string if none are active.""" - return self.get_child_by_type(Tabs).active + def validate_active(self, active: str) -> str: + """It doesn't make sense for `active` to be an empty string. + + Args: + active: Attribute to be validated. + + Returns: + Value of `active`. - @active.setter - def active(self, active: str) -> None: + Raises: + ValueError: If the active attribute is set to empty string. + """ if not active: raise ValueError("'active' tab must not be empty string.") - self.get_child_by_type(Tabs).active = active + return active def compose(self) -> ComposeResult: """Compose the tabbed content.""" @@ -178,6 +186,7 @@ def _on_tabs_tab_activated(self, event: Tabs.TabActivated) -> None: switcher = self.get_child_by_type(ContentSwitcher) assert isinstance(event.tab, ContentTab) switcher.current = event.tab.id + self.active = event.tab.id self.post_message( TabbedContent.TabActivated( tabbed_content=self, @@ -188,3 +197,7 @@ def _on_tabs_tab_activated(self, event: Tabs.TabActivated) -> None: def _on_tabs_cleared(self, event: Tabs.Cleared) -> None: """All tabs were removed.""" event.stop() + + def watch_active(self, active: str) -> None: + """Switch tabs when the active attributes changes.""" + self.get_child_by_type(Tabs).active = active diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index c80298cc6b..5cd124486e 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -19676,165 +19676,6 @@ ''' # --- -# name: test_quickly_change_tabs - ''' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - QuicklyChangeTabsApp - - - - - - - - - - - onetwothree - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - three - - - - - - - - - - - - - - - - - - - - - - - - ''' -# --- # name: test_radio_button_example ''' diff --git a/tests/snapshot_tests/snapshot_apps/quickly_change_tabs.py b/tests/snapshot_tests/snapshot_apps/quickly_change_tabs.py deleted file mode 100644 index d15433805e..0000000000 --- a/tests/snapshot_tests/snapshot_apps/quickly_change_tabs.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Regression test for https://github.com/Textualize/textual/issues/2229.""" -from textual.app import App, ComposeResult -from textual.widgets import TabbedContent, TabPane, Tabs, Label - - -class QuicklyChangeTabsApp(App[None]): - def compose(self) -> ComposeResult: - with TabbedContent(): - with TabPane("one"): - yield Label("one") - with TabPane("two"): - yield Label("two") - with TabPane("three", id="three"): - yield Label("three") - - def key_p(self) -> None: - self.query_one(Tabs).action_next_tab() - self.query_one(Tabs).action_next_tab() - - -app = QuicklyChangeTabsApp() - -if __name__ == "__main__": - app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 16001300b4..2ab2ba63c3 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -431,8 +431,3 @@ def test_scroll_to_center(snap_compare): # scrolled so that the red string >>bullseye<< is centered on the screen. # When this snapshot "breaks" because #2254 is fixed, this snapshot can be updated. assert snap_compare(SNAPSHOT_APPS_DIR / "scroll_to_center.py", press=["s"]) - - -def test_quickly_change_tabs(snap_compare): - # https://github.com/Textualize/textual/issues/2229 - assert snap_compare(SNAPSHOT_APPS_DIR / "quickly_change_tabs.py", press=["p"])