From 1759f2c1f9a6984efa376db8a790281bf71636dd Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:13:49 +0000 Subject: [PATCH 01/41] feat(document): add start and end properties --- src/textual/document/_document.py | 21 +++++++++++++++++++++ src/textual/widgets/_text_area.py | 5 +---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/textual/document/_document.py b/src/textual/document/_document.py index 3a4e5729b2..591e9f1c56 100644 --- a/src/textual/document/_document.py +++ b/src/textual/document/_document.py @@ -171,6 +171,16 @@ def prepare_query(self, query: str) -> Query | None: def line_count(self) -> int: """Returns the number of lines in the document.""" + @property + @abstractmethod + def start(self) -> Location: + """Returns the location of the start of the document (0, 0).""" + + @property + @abstractmethod + def end(self) -> Location: + """Returns the location of the end of the document.""" + @overload def __getitem__(self, line_index: int) -> str: ... @@ -331,6 +341,17 @@ def line_count(self) -> int: """Returns the number of lines in the document.""" return len(self._lines) + @property + def start(self) -> Location: + """Returns the location of the start of the document (0, 0).""" + return (0, 0) + + @property + def end(self) -> Location: + """Returns the location of the end of the document.""" + last_line = self._lines[-1] + return (self.line_count, len(last_line)) + def get_index_from_location(self, location: Location) -> int: """Given a location, returns the index from the document's text. diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index e6bce9aa29..b770464bc7 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -2011,10 +2011,7 @@ def clear(self) -> EditResult: Returns: An EditResult relating to the deletion of all content. """ - document = self.document - last_line = document[-1] - document_end = (document.line_count, len(last_line)) - return self.delete((0, 0), document_end, maintain_selection_offset=False) + return self.delete((0, 0), self.document.end, maintain_selection_offset=False) def _delete_via_keyboard( self, From cb65d6e3b1b90c3caa3cb9a46f65d65d2debbe90 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:41:10 +0000 Subject: [PATCH 02/41] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3d4909de..12e1c696a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `Pilot.resize_terminal` to resize the terminal in testing https://github.com/Textualize/textual/issues/4212 - Added `sort_children` method https://github.com/Textualize/textual/pull/4244 - Support for pseudo-classes in nested TCSS https://github.com/Textualize/textual/issues/4039 +- Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 ### Fixed From 11443e8918597b69602f3672191b3d858c7cfc46 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Wed, 6 Mar 2024 21:24:46 +0000 Subject: [PATCH 03/41] add tip to text area editing docs --- docs/widgets/text_area.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/widgets/text_area.md b/docs/widgets/text_area.md index c0a95c265a..9e24442d30 100644 --- a/docs/widgets/text_area.md +++ b/docs/widgets/text_area.md @@ -84,6 +84,10 @@ This method is the programmatic equivalent of selecting some text and then pasti Some other convenient methods are available, such as [`insert`][textual.widgets._text_area.TextArea.insert], [`delete`][textual.widgets._text_area.TextArea.delete], and [`clear`][textual.widgets._text_area.TextArea.clear]. +!!! tip + The `TextArea.document.end` property returns the location at the end of the + document, which might be convenient when editing programmatically. + ### Working with the cursor #### Moving the cursor From 8a995fbcc131c1355a06e4d9b22c462c881407b9 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 19 Mar 2024 13:39:26 +0000 Subject: [PATCH 04/41] Fix a crash in run_action when an action is an empty tuple Fixes #4248. --- src/textual/app.py | 4 +++- tests/test_issue_4248.py | 46 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/test_issue_4248.py diff --git a/src/textual/app.py b/src/textual/app.py index aaf799a66c..b79674e713 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -2926,7 +2926,9 @@ async def run_action( if isinstance(action, str): target, params = actions.parse(action) else: - target, params = action + # `action` can end up coming in as (), so if that happens we'll + # ask the action parser for a correctly-formed empty action. + target, params = action or actions.parse("") implicit_destination = True if "." in target: destination, action_name = target.split(".", 1) diff --git a/tests/test_issue_4248.py b/tests/test_issue_4248.py new file mode 100644 index 0000000000..de3eee0928 --- /dev/null +++ b/tests/test_issue_4248.py @@ -0,0 +1,46 @@ +"""Test https://github.com/Textualize/textual/issues/4248""" + +from textual.app import App, ComposeResult +from textual.widgets import Label + + +async def test_issue_4248() -> None: + """Various forms of click parameters should be fine.""" + + bumps = 0 + + class ActionApp(App[None]): + + def compose(self) -> ComposeResult: + yield Label("[@click]click me and crash[/]", id="nothing") + yield Label("[@click=]click me and crash[/]", id="no-params") + yield Label("[@click=()]click me and crash[/]", id="empty-params") + yield Label("[@click=foobar]click me[/]", id="unknown-sans-parens") + yield Label("[@click=foobar()]click me[/]", id="unknown-with-parens") + yield Label("[@click=bump]click me[/]", id="known-sans-parens") + yield Label("[@click=bump()]click me[/]", id="known-empty-parens") + yield Label("[@click=bump(100)]click me[/]", id="known-with-param") + + def action_bump(self, by_value: int = 1) -> None: + nonlocal bumps + bumps += by_value + + app = ActionApp() + async with app.run_test() as pilot: + assert bumps == 0 + await pilot.click("#nothing") + assert bumps == 0 + await pilot.click("#no-params") + assert bumps == 0 + await pilot.click("#empty-params") + assert bumps == 0 + await pilot.click("#unknown-sans-parens") + assert bumps == 0 + await pilot.click("#unknown-with-parens") + assert bumps == 0 + await pilot.click("#known-sans-parens") + assert bumps == 1 + await pilot.click("#known-empty-parens") + assert bumps == 2 + await pilot.click("#known-with-param") + assert bumps == 102 From 90c1a8b7fc55e1028122c6adcdd53ecc226a898e Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 19 Mar 2024 14:23:01 +0000 Subject: [PATCH 05/41] Move responsibility for the empty action to App._broken_event --- src/textual/app.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index b79674e713..88bb5843de 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -2926,9 +2926,7 @@ async def run_action( if isinstance(action, str): target, params = actions.parse(action) else: - # `action` can end up coming in as (), so if that happens we'll - # ask the action parser for a correctly-formed empty action. - target, params = action or actions.parse("") + target, params = action implicit_destination = True if "." in target: destination, action_name = target.split(".", 1) @@ -3008,11 +3006,16 @@ async def _broker_event( return False else: event.stop() - if isinstance(action, (str, tuple)): + if isinstance(action, str) or (isinstance(action, tuple) and len(action) == 2): await self.run_action(action, default_namespace=default_namespace) # type: ignore[arg-type] elif callable(action): await action() else: + if isinstance(action, tuple) and self.debug: + # It's a tuple and made it this far, which means it'll be a + # malformed action. This is a no-op, but let's log that + # anyway. + log.warning(f"{event_name} event has an empty a action!") return False return True From 564010f273a2b12644fbd0216b9fd6187df575d2 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 19 Mar 2024 14:35:18 +0000 Subject: [PATCH 06/41] Improve the log text Also make it read something akin to English. O_o --- src/textual/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/app.py b/src/textual/app.py index 88bb5843de..942bf8b376 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -3015,7 +3015,7 @@ async def _broker_event( # It's a tuple and made it this far, which means it'll be a # malformed action. This is a no-op, but let's log that # anyway. - log.warning(f"{event_name} event has an empty a action!") + log.warning(f"{event_name} event has an empty action") return False return True From 1ae6493a1618421ac23de3efbd878f64aed149cb Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 20 Mar 2024 11:46:35 +0000 Subject: [PATCH 07/41] fix for flicker --- src/textual/widget.py | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/textual/widget.py b/src/textual/widget.py index 4a27762f7d..8b37c3752c 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -361,8 +361,6 @@ def __init__( self._styles_cache = StylesCache() self._rich_style_cache: dict[str, tuple[Style, Style]] = {} - self._stabilize_scrollbar: tuple[Size, str, str] | None = None - """Used to prevent scrollbar logic getting stuck in an infinite loop.""" self._tooltip: RenderableType | None = None """The tooltip content.""" @@ -1437,14 +1435,6 @@ def _refresh_scrollbars(self) -> None: overflow_x = styles.overflow_x overflow_y = styles.overflow_y - stabilize_scrollbar = ( - self.container_size, - overflow_x, - overflow_y, - ) - if self._stabilize_scrollbar == stabilize_scrollbar: - return - width, height = self._container_size show_horizontal = False @@ -1464,16 +1454,15 @@ def _refresh_scrollbars(self) -> None: show_vertical = self.virtual_size.height > height # When a single scrollbar is shown, the other dimension changes, so we need to recalculate. - if overflow_x == "auto" and show_vertical and not show_horizontal: - show_horizontal = self.virtual_size.width > ( - width - styles.scrollbar_size_vertical - ) - if overflow_y == "auto" and show_horizontal and not show_vertical: - show_vertical = self.virtual_size.height > ( - height - styles.scrollbar_size_horizontal - ) - - self._stabilize_scrollbar = stabilize_scrollbar + if overflow_x != "auto" or overflow_y != "auto": + if overflow_x == "auto" and show_vertical and not show_horizontal: + show_horizontal = self.virtual_size.width > ( + width - styles.scrollbar_size_vertical + ) + if overflow_y == "auto" and show_horizontal and not show_vertical: + show_vertical = self.virtual_size.height > ( + height - styles.scrollbar_size_horizontal + ) self.show_horizontal_scrollbar = show_horizontal self.show_vertical_scrollbar = show_vertical @@ -3322,7 +3311,6 @@ def refresh( return self if layout: self._layout_required = True - self._stabilize_scrollbar = None for ancestor in self.ancestors: if not isinstance(ancestor, Widget): break From 889615ccde807d5946ad3808abc25c06baa51064 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 20 Mar 2024 11:52:04 +0000 Subject: [PATCH 08/41] snapshot --- .../__snapshots__/test_snapshots.ambr | 187 +++++++++--------- 1 file changed, 94 insertions(+), 93 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index cfe5b99be1..4bb6d2a56c 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -1164,202 +1164,203 @@ font-weight: 700; } - .terminal-3752476664-matrix { + .terminal-168952470-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3752476664-title { + .terminal-168952470-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3752476664-r1 { fill: #c5c8c6 } - .terminal-3752476664-r2 { fill: #e3e3e3 } - .terminal-3752476664-r3 { fill: #004578 } - .terminal-3752476664-r4 { fill: #e1e1e1 } - .terminal-3752476664-r5 { fill: #632ca6 } - .terminal-3752476664-r6 { fill: #dde6ed;font-weight: bold } - .terminal-3752476664-r7 { fill: #14191f } - .terminal-3752476664-r8 { fill: #23568b } + .terminal-168952470-r1 { fill: #c5c8c6 } + .terminal-168952470-r2 { fill: #e3e3e3 } + .terminal-168952470-r3 { fill: #004578 } + .terminal-168952470-r4 { fill: #e1e1e1 } + .terminal-168952470-r5 { fill: #632ca6 } + .terminal-168952470-r6 { fill: #dde6ed;font-weight: bold } + .terminal-168952470-r7 { fill: #14191f } + .terminal-168952470-r8 { fill: #1e1e1e } + .terminal-168952470-r9 { fill: #23568b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MyApp + MyApp - + - - MyApp - ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - oktest - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ -  0 ────────────────────────────────────── 1 ────────────────────────────────────── 2 ───── - -  Foo       Bar         Baz               Foo       Bar         Baz               Foo      -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH - ───────────────────────────────────────────────────────────────────────────────────────────── - - ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + + MyApp + ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + oktest + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ +  0 ────────────────────────────────────── 1 ────────────────────────────────────── 2 ───── + +  Foo       Bar         Baz               Foo       Bar         Baz               Foo      +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH + ───────────────────────────────────────────────────────────────────────────────────────────── + + ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── From e63e2b8014c5d50f308e40ce314a8fd8682a8b67 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 20 Mar 2024 12:43:59 +0000 Subject: [PATCH 09/41] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d15c26cd31..80dc5d2f81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fixed a crash in `TextArea` when undoing an edit to a selection the selection was made backwards https://github.com/Textualize/textual/issues/4301 +- Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 ## [0.53.1] - 2023-03-18 From 103915c23632a9e99a3f7c2d85c765e022e2c791 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 21 Mar 2024 13:42:54 +0000 Subject: [PATCH 10/41] Add a snapshot test for the Welcome widget --- .../__snapshots__/test_snapshots.ambr | 162 ++++++++++++++++++ .../snapshot_apps/welcome_widget.py | 10 ++ tests/snapshot_tests/test_snapshots.py | 5 + 3 files changed, 177 insertions(+) create mode 100644 tests/snapshot_tests/snapshot_apps/welcome_widget.py diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 0d054a47ee..4aea31575d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -42458,6 +42458,168 @@ ''' # --- +# name: test_welcome + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WelcomeApp + + + + + + + + + + + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + Welcome! + ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + Textual is a TUI, or Text User Interface, framework for Python inspired by   + modern web development. We hope you enjoy using Textual! + + + Dune quote + + ▌ "I must not fear.Fear is the mind-killer.Fear is the little-death that + ▌ brings total obliteration.I will face my fear.I will permit it to pass + ▌ over me and through me.And when it has gone past, I will turn the inner + ▌ eye to see its path.Where the fear has gone there will be nothing. Only + ▌ I will remain." + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + OK + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + ''' +# --- # name: test_zero_scrollbar_size ''' diff --git a/tests/snapshot_tests/snapshot_apps/welcome_widget.py b/tests/snapshot_tests/snapshot_apps/welcome_widget.py new file mode 100644 index 0000000000..b82287dcd6 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/welcome_widget.py @@ -0,0 +1,10 @@ +from textual.app import App, ComposeResult +from textual.widgets import Welcome + +class WelcomeApp(App[None]): + + def compose(self) -> ComposeResult: + yield Welcome() + +if __name__ == "__main__": + WelcomeApp().run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index d33a4178f3..697e88f173 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -1162,3 +1162,8 @@ def test_button_widths(snap_compare): """Test that button widths expand auto containers as expected.""" # https://github.com/Textualize/textual/issues/4024 assert snap_compare(SNAPSHOT_APPS_DIR / "button_widths.py") + + +def test_welcome(snap_compare): + assert snap_compare(SNAPSHOT_APPS_DIR / "welcome_widget.py") + From f8fd9b4245079b700a8e09dbd30d3ba6c0fc43ae Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 21 Mar 2024 13:44:44 +0000 Subject: [PATCH 11/41] sctrollbars --- src/textual/screen.py | 2 +- src/textual/signal.py | 4 +- src/textual/widget.py | 23 ++- .../__snapshots__/test_snapshots.ambr | 187 +++++++++--------- 4 files changed, 117 insertions(+), 99 deletions(-) diff --git a/src/textual/screen.py b/src/textual/screen.py index d29022d3a5..b9713e7256 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -38,6 +38,7 @@ from .css.parse import parse_selectors from .css.query import NoMatches, QueryType from .dom import DOMNode +from .errors import NoWidget from .geometry import Offset, Region, Size from .reactive import Reactive from .renderables.background_screen import BackgroundScreen @@ -54,7 +55,6 @@ from .command import Provider # Unused & ignored imports are needed for the docs to link to these objects: - from .errors import NoWidget # type: ignore # noqa: F401 from .message_pump import MessagePump # Screen updates will be batched so that they don't happen more often than 60 times per second: diff --git a/src/textual/signal.py b/src/textual/signal.py index 6226b0273b..c08f87ffb2 100644 --- a/src/textual/signal.py +++ b/src/textual/signal.py @@ -88,4 +88,6 @@ def publish(self) -> None: try: callback() except Exception as error: - log.error(f"error publishing signal to {node} ignored; {error}") + log.error( + f"error publishing signal to {node} ignored; {callback}; {error}" + ) diff --git a/src/textual/widget.py b/src/textual/widget.py index 8b37c3752c..aac76e8778 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -367,6 +367,9 @@ def __init__( self._absolute_offset: Offset | None = None """Force an absolute offset for the widget (used by tooltips).""" + self._scrollbar_changes: set[tuple[bool, bool]] = set() + """Used to stabilize scrollbars.""" + super().__init__( name=name, id=id, @@ -797,7 +800,6 @@ def _arrange(self, size: Size) -> DockArrangeResult: def _clear_arrangement_cache(self) -> None: """Clear arrangement cache, forcing a new arrange operation.""" self._arrangement_cache.clear() - self._stabilize_scrollbar = None def _get_virtual_dom(self) -> Iterable[Widget]: """Get widgets not part of the DOM. @@ -1453,8 +1455,15 @@ def _refresh_scrollbars(self) -> None: elif overflow_y == "auto": show_vertical = self.virtual_size.height > height - # When a single scrollbar is shown, the other dimension changes, so we need to recalculate. - if overflow_x != "auto" or overflow_y != "auto": + _show_horizontal = show_horizontal + _show_vertical = show_vertical + + if not ( + overflow_x == "auto" + and overflow_y == "auto" + and (show_horizontal, show_vertical) in self._scrollbar_changes + ): + # When a single scrollbar is shown, the other dimension changes, so we need to recalculate. if overflow_x == "auto" and show_vertical and not show_horizontal: show_horizontal = self.virtual_size.width > ( width - styles.scrollbar_size_vertical @@ -1464,6 +1473,14 @@ def _refresh_scrollbars(self) -> None: height - styles.scrollbar_size_horizontal ) + if ( + self.show_horizontal_scrollbar != show_horizontal + or self.show_vertical_scrollbar != show_vertical + ): + self._scrollbar_changes.add((_show_horizontal, _show_vertical)) + else: + self._scrollbar_changes.clear() + self.show_horizontal_scrollbar = show_horizontal self.show_vertical_scrollbar = show_vertical diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index b8764da7cc..0d054a47ee 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -1164,203 +1164,202 @@ font-weight: 700; } - .terminal-168952470-matrix { + .terminal-3752476664-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-168952470-title { + .terminal-3752476664-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-168952470-r1 { fill: #c5c8c6 } - .terminal-168952470-r2 { fill: #e3e3e3 } - .terminal-168952470-r3 { fill: #004578 } - .terminal-168952470-r4 { fill: #e1e1e1 } - .terminal-168952470-r5 { fill: #632ca6 } - .terminal-168952470-r6 { fill: #dde6ed;font-weight: bold } - .terminal-168952470-r7 { fill: #14191f } - .terminal-168952470-r8 { fill: #1e1e1e } - .terminal-168952470-r9 { fill: #23568b } + .terminal-3752476664-r1 { fill: #c5c8c6 } + .terminal-3752476664-r2 { fill: #e3e3e3 } + .terminal-3752476664-r3 { fill: #004578 } + .terminal-3752476664-r4 { fill: #e1e1e1 } + .terminal-3752476664-r5 { fill: #632ca6 } + .terminal-3752476664-r6 { fill: #dde6ed;font-weight: bold } + .terminal-3752476664-r7 { fill: #14191f } + .terminal-3752476664-r8 { fill: #23568b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MyApp + MyApp - + - - MyApp - ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - oktest - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ -  0 ────────────────────────────────────── 1 ────────────────────────────────────── 2 ───── - -  Foo       Bar         Baz               Foo       Bar         Baz               Foo      -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH - ───────────────────────────────────────────────────────────────────────────────────────────── - - ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + + MyApp + ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + oktest + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ +  0 ────────────────────────────────────── 1 ────────────────────────────────────── 2 ───── + +  Foo       Bar         Baz               Foo       Bar         Baz               Foo      +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH + ───────────────────────────────────────────────────────────────────────────────────────────── + + ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── From b8802e18780844e40bc6e81df538a0feb267999b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 21 Mar 2024 13:47:16 +0000 Subject: [PATCH 12/41] error message --- src/textual/signal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/signal.py b/src/textual/signal.py index c08f87ffb2..a1a1d80b8a 100644 --- a/src/textual/signal.py +++ b/src/textual/signal.py @@ -89,5 +89,5 @@ def publish(self) -> None: callback() except Exception as error: log.error( - f"error publishing signal to {node} ignored; {callback}; {error}" + f"error publishing signal to {node} ignored (callback={callback}); {error}" ) From 5170a64bb2271924ca7a6b8650177119b925029f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 20 Mar 2024 14:47:45 +0000 Subject: [PATCH 13/41] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c2d653e06..33369556e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed a crash in `TextArea` when undoing an edit to a selection the selection was made backwards https://github.com/Textualize/textual/issues/4301 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 + +### Changed + - ProgressBar won't show ETA until there is at least one second of samples https://github.com/Textualize/textual/pull/4316 ## [0.53.1] - 2023-03-18 From 62d1ec45571e2f07d664562594365955722d8e36 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 21 Mar 2024 14:37:11 +0000 Subject: [PATCH 14/41] Collapse spaces and tabs in text in Markdown See #4321. --- src/textual/widgets/_markdown.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index c37c7770e0..6ddcef4319 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re from pathlib import Path, PurePath from typing import Callable, Iterable, Optional @@ -146,10 +147,14 @@ def build_from_token(self, token: Token) -> None: self._token = token style_stack: list[Style] = [Style()] content = Text() + repeating_whitespace = re.compile(r"[ \t]+") # Space or tabs get reduced. if token.children: for child in token.children: if child.type == "text": - content.append(child.content, style_stack[-1]) + content.append( + re.sub(repeating_whitespace, " ", child.content), + style_stack[-1], + ) if child.type == "hardbreak": content.append("\n") if child.type == "softbreak": From 6e8512feb03e04bfa2f3fa2e0ab803a5591d2c38 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 21 Mar 2024 15:20:30 +0000 Subject: [PATCH 15/41] Add snapshot testing for Markdown whitespace squishing --- .../__snapshots__/test_snapshots.ambr | 160 ++++++++++++++++++ .../snapshot_apps/markdown_whitespace.py | 49 ++++++ tests/snapshot_tests/test_snapshots.py | 3 + 3 files changed, 212 insertions(+) create mode 100644 tests/snapshot_tests/snapshot_apps/markdown_whitespace.py diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 0d054a47ee..36ff419200 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -23647,6 +23647,166 @@ ''' # --- +# name: test_markdown_space_squashing + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MarkdownSpaceApp + + + + + + + + + + X XX XX X X X X X + + X XX XX X X X X X + + X XX X X X X X + + X XX X X X X X + + + + + + + + + + + + + + + + + + + + + + ''' +# --- # name: test_markdown_theme_switching ''' diff --git a/tests/snapshot_tests/snapshot_apps/markdown_whitespace.py b/tests/snapshot_tests/snapshot_apps/markdown_whitespace.py new file mode 100644 index 0000000000..c0520e7e37 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/markdown_whitespace.py @@ -0,0 +1,49 @@ +from textual.app import App, ComposeResult +from textual.widgets import Markdown + +MARKDOWN = ( + """\ +X X + +X X + +X\tX + +X\t\tX +""", + """\ +X \tX + +X \t \tX +""", + """\ +[X X X\tX\t\tX \t \tX](https://example.com/) + +_X X X\tX\t\tX \t \tX_ + +**X X X\tX\t\tX \t \tX** + +~~X X X\tX\t\tX \t \tX~~ +""" +) + +class MarkdownSpaceApp(App[None]): + + CSS = """ + Screen { + layout: horizontal; + } + Markdown { + margin-left: 0; + border-left: solid red; + width: 1fr; + height: 1fr; + } + """ + + def compose(self) -> ComposeResult: + for document in MARKDOWN: + yield Markdown(document) + +if __name__ == "__main__": + MarkdownSpaceApp().run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index d33a4178f3..abe89d2fc8 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -686,6 +686,9 @@ async def run_before(pilot): run_before=run_before, ) +def test_markdown_space_squashing(snap_compare): + assert snap_compare(SNAPSHOT_APPS_DIR / "markdown_whitespace.py") + def test_layer_fix(snap_compare): # Check https://github.com/Textualize/textual/issues/1358 From 0907da1e2dadeeb37785640115d284bd6343de34 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 21 Mar 2024 15:33:23 +0000 Subject: [PATCH 16/41] Final form of the warning --- src/textual/app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/textual/app.py b/src/textual/app.py index 942bf8b376..15811232c9 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -3015,7 +3015,9 @@ async def _broker_event( # It's a tuple and made it this far, which means it'll be a # malformed action. This is a no-op, but let's log that # anyway. - log.warning(f"{event_name} event has an empty action") + log.warning( + f"Can't parse @{event_name} action from style meta; check your console markup syntax" + ) return False return True From 0955254ca93a793ccc131d78f7a1194470b08e74 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 23 Mar 2024 10:59:33 +0000 Subject: [PATCH 17/41] clear on size updte --- src/textual/scroll_view.py | 1 + src/textual/widgets/_sparkline.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index 4e418ed350..88493f3ed1 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -89,6 +89,7 @@ def _size_updated( or virtual_size != self.virtual_size or container_size != self.container_size ): + self._scrollbar_changes.clear() self._size = size virtual_size = self.virtual_size self._container_size = size - self.styles.gutter.totals diff --git a/src/textual/widgets/_sparkline.py b/src/textual/widgets/_sparkline.py index f3315ba153..d691f5f435 100644 --- a/src/textual/widgets/_sparkline.py +++ b/src/textual/widgets/_sparkline.py @@ -66,7 +66,7 @@ def __init__( Args: data: The initial data to populate the sparkline with. - summary_function: Summarises bar values into a single value used to + summary_function: Summarizes bar values into a single value used to represent each bar. name: The name of the widget. id: The ID of the widget in the DOM. From c177d133fff290d764f82e609869437e7789b436 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 25 Mar 2024 10:39:22 +0000 Subject: [PATCH 18/41] Add fenced code blocks to the snapshot test --- .../__snapshots__/test_snapshots.ambr | 130 ++++++++++-------- .../snapshot_apps/markdown_whitespace.py | 20 ++- 2 files changed, 84 insertions(+), 66 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 36ff419200..7f698277a5 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -23670,137 +23670,145 @@ font-weight: 700; } - .terminal-3324853287-matrix { + .terminal-2455550894-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3324853287-title { + .terminal-2455550894-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3324853287-r1 { fill: #ff0000 } - .terminal-3324853287-r2 { fill: #e1e1e1 } - .terminal-3324853287-r3 { fill: #c5c8c6 } - .terminal-3324853287-r4 { fill: #e1e1e1;text-decoration: underline; } - .terminal-3324853287-r5 { fill: #e1e1e1;font-style: italic; } - .terminal-3324853287-r6 { fill: #e1e1e1;font-weight: bold } - .terminal-3324853287-r7 { fill: #e1e1e1;text-decoration: line-through; } + .terminal-2455550894-r1 { fill: #ff0000 } + .terminal-2455550894-r2 { fill: #e1e1e1 } + .terminal-2455550894-r3 { fill: #c5c8c6 } + .terminal-2455550894-r4 { fill: #e1e1e1;text-decoration: underline; } + .terminal-2455550894-r5 { fill: #e1e1e1;font-style: italic; } + .terminal-2455550894-r6 { fill: #e1e1e1;font-weight: bold } + .terminal-2455550894-r7 { fill: #e1e1e1;text-decoration: line-through; } + .terminal-2455550894-r8 { fill: #d2d2d2 } + .terminal-2455550894-r9 { fill: #546e7a;font-style: italic; } + .terminal-2455550894-r10 { fill: #bb80b3 } + .terminal-2455550894-r11 { fill: #eeffff } + .terminal-2455550894-r12 { fill: #ffcb6b } + .terminal-2455550894-r13 { fill: #89ddff } + .terminal-2455550894-r14 { fill: #41565f;font-style: italic; } + .terminal-2455550894-r15 { fill: #f78c6c } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarkdownSpaceApp + MarkdownSpaceApp - - - - X XX XX X X X X X - - X XX XX X X X X X - - X XX X X X X X - - X XX X X X X X - - - - - - - - - - - - - - - - - + + + + X XX XX X X X X X + + X XX XX X X X X X + + X XX X X X X X + + X XX X X X X X + + ─────────────────────────────────────────────────────────────────────────────── + + + # Two spaces:  see? + classFoo: + │   '''This is    a doc    string.''' + │   some_code(1,2,3,4) + + + + + + + + + diff --git a/tests/snapshot_tests/snapshot_apps/markdown_whitespace.py b/tests/snapshot_tests/snapshot_apps/markdown_whitespace.py index c0520e7e37..71c8fd8bbb 100644 --- a/tests/snapshot_tests/snapshot_apps/markdown_whitespace.py +++ b/tests/snapshot_tests/snapshot_apps/markdown_whitespace.py @@ -1,4 +1,5 @@ from textual.app import App, ComposeResult +from textual.containers import Horizontal from textual.widgets import Markdown MARKDOWN = ( @@ -30,20 +31,29 @@ class MarkdownSpaceApp(App[None]): CSS = """ - Screen { - layout: horizontal; - } Markdown { margin-left: 0; border-left: solid red; width: 1fr; height: 1fr; } + .code { + height: 2fr; + border-top: solid red; + } """ def compose(self) -> ComposeResult: - for document in MARKDOWN: - yield Markdown(document) + with Horizontal(): + for document in MARKDOWN: + yield Markdown(document) + yield Markdown("""```python +# Two spaces: see? +class Foo: + '''This is a doc string.''' + some_code(1, 2, 3, 4) +``` +""", classes="code") if __name__ == "__main__": MarkdownSpaceApp().run() From f85efe3005440ef9b424dd0085a117b19a92f02d Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 25 Mar 2024 11:29:03 +0000 Subject: [PATCH 19/41] Tweak comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> --- src/textual/widgets/_markdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 6ddcef4319..7ef1abd18a 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -147,7 +147,7 @@ def build_from_token(self, token: Token) -> None: self._token = token style_stack: list[Style] = [Style()] content = Text() - repeating_whitespace = re.compile(r"[ \t]+") # Space or tabs get reduced. + repeating_whitespace = re.compile(r"[ \t]+") # Spaces and tabs get reduced. if token.children: for child in token.children: if child.type == "text": From 30ff0e8ce54e88711d3349d84f02036ace729dee Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 25 Mar 2024 11:35:50 +0000 Subject: [PATCH 20/41] Let the re module RE cache to the work --- src/textual/widgets/_markdown.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 7ef1abd18a..f11e76e933 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -147,12 +147,13 @@ def build_from_token(self, token: Token) -> None: self._token = token style_stack: list[Style] = [Style()] content = Text() - repeating_whitespace = re.compile(r"[ \t]+") # Spaces and tabs get reduced. if token.children: for child in token.children: if child.type == "text": content.append( - re.sub(repeating_whitespace, " ", child.content), + # Ensure repeating spaces and/or tabs get squashed + # down to a single space. + re.sub(r"[ \t]+", " ", child.content), style_stack[-1], ) if child.type == "hardbreak": From 2b2e2a8436f8bfb509a388f393a052b1416d6271 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:26:18 +0000 Subject: [PATCH 21/41] add start default implementation --- src/textual/document/_document.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/textual/document/_document.py b/src/textual/document/_document.py index 591e9f1c56..ca850285b5 100644 --- a/src/textual/document/_document.py +++ b/src/textual/document/_document.py @@ -175,6 +175,7 @@ def line_count(self) -> int: @abstractmethod def start(self) -> Location: """Returns the location of the start of the document (0, 0).""" + return (0, 0) @property @abstractmethod @@ -344,7 +345,7 @@ def line_count(self) -> int: @property def start(self) -> Location: """Returns the location of the start of the document (0, 0).""" - return (0, 0) + return super().start @property def end(self) -> Location: From 667b1e81c00c6774296734ad6ab6e0ac85596355 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:28:19 +0000 Subject: [PATCH 22/41] update changelog --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a33d1761..7fb3ac6f5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 -### Changed +### Changed - ProgressBar won't show ETA until there is at least one second of samples https://github.com/Textualize/textual/pull/4316 +### Added + +- Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 + ## [0.53.1] - 2023-03-18 ### Fixed @@ -31,7 +35,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `Pilot.resize_terminal` to resize the terminal in testing https://github.com/Textualize/textual/issues/4212 - Added `sort_children` method https://github.com/Textualize/textual/pull/4244 - Support for pseudo-classes in nested TCSS https://github.com/Textualize/textual/issues/4039 -- Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 ### Fixed From 352d9913ed517baeb4f18ad5ec57b4a7ee83689f Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 25 Mar 2024 14:02:18 +0000 Subject: [PATCH 23/41] Fall back to the most basic whitespace matching Seems we're happy that \s along won't be too greedy. --- src/textual/widgets/_markdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index f11e76e933..849e291bd4 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -153,7 +153,7 @@ def build_from_token(self, token: Token) -> None: content.append( # Ensure repeating spaces and/or tabs get squashed # down to a single space. - re.sub(r"[ \t]+", " ", child.content), + re.sub(r"\s+", " ", child.content), style_stack[-1], ) if child.type == "hardbreak": From a80f3089dd33d2cdc45dbc91b36b788dd519a751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:15:03 +0000 Subject: [PATCH 24/41] Let pilot reraise exceptions from Widget.compose. This is probably an edge-case that wasn't covered in the original PR that introduced the machinery (namely App._exception and App._exception_event) that I want to leverage here. Related PRs: #2754 --- src/textual/widget.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/textual/widget.py b/src/textual/widget.py index aac76e8778..4bfaabdee0 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3618,10 +3618,8 @@ async def _compose(self) -> None: raise TypeError( f"{self!r} compose() method returned an invalid result; {error}" ) from error - except Exception: - from rich.traceback import Traceback - - self.app.panic(Traceback()) + except Exception as error: + self.app._handle_exception(error) else: self._extend_compose(widgets) await self.mount_composed_widgets(widgets) From 74ab96763fdb02583b92050ea58d897ed3d848d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:37:44 +0000 Subject: [PATCH 25/41] Add test to ensure exception reraised in tests. --- CHANGELOG.md | 1 + tests/test_pilot.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a0cb30a51..3239310763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed a crash in `TextArea` when undoing an edit to a selection the selection was made backwards https://github.com/Textualize/textual/issues/4301 - Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 +- Exceptions inside `Widget.compose` weren't bubbling up in tests https://github.com/Textualize/textual/issues/4282 ### Changed diff --git a/tests/test_pilot.py b/tests/test_pilot.py index d436f8b5fc..10ea623453 100644 --- a/tests/test_pilot.py +++ b/tests/test_pilot.py @@ -7,6 +7,7 @@ from textual.binding import Binding from textual.containers import Center, Middle from textual.pilot import OutOfBounds +from textual.screen import Screen from textual.widgets import Button, Label KEY_CHARACTERS_TO_TEST = "akTW03" + punctuation @@ -56,7 +57,7 @@ def on_key(self, event: events.Key) -> None: assert keys_pressed[-1] == char -async def test_pilot_exception_catching_compose(): +async def test_pilot_exception_catching_app_compose(): """Ensuring that test frameworks are aware of exceptions inside compose methods when running via Pilot run_test().""" @@ -70,6 +71,21 @@ def compose(self) -> ComposeResult: pass +async def test_pilot_exception_cathing_widget_compose(): + class SomeScreen(Screen[None]): + def compose(self) -> ComposeResult: + 1 / 0 + yield Label("Beep") + + class FailingApp(App[None]): + def on_mount(self) -> None: + self.push_screen(SomeScreen()) + + with pytest.raises(ZeroDivisionError): + async with FailingApp().run_test(): + pass + + async def test_pilot_exception_catching_action(): """Ensure that exceptions inside action handlers are presented to the test framework when running via Pilot run_test().""" From 8c48a3b95d6b14ec20bf701a5d1e6a699be58edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:25:09 +0000 Subject: [PATCH 26/41] Surface exceptions from workers to testing frameworks. --- CHANGELOG.md | 2 +- src/textual/worker.py | 2 +- tests/test_pilot.py | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3239310763..ecbc5dd4b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed a crash in `TextArea` when undoing an edit to a selection the selection was made backwards https://github.com/Textualize/textual/issues/4301 - Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 -- Exceptions inside `Widget.compose` weren't bubbling up in tests https://github.com/Textualize/textual/issues/4282 +- Exceptions inside `Widget.compose` or workers weren't bubbling up in tests https://github.com/Textualize/textual/issues/4282 ### Changed diff --git a/src/textual/worker.py b/src/textual/worker.py index 7719cd4dca..6cef3f6b90 100644 --- a/src/textual/worker.py +++ b/src/textual/worker.py @@ -375,7 +375,7 @@ async def _run(self, app: App) -> None: app.log.worker(Traceback()) if self.exit_on_error: - app._fatal_error() + app._handle_exception(error) else: self.state = WorkerState.SUCCESS app.log.worker(self) diff --git a/tests/test_pilot.py b/tests/test_pilot.py index 10ea623453..4a95b9c0fc 100644 --- a/tests/test_pilot.py +++ b/tests/test_pilot.py @@ -2,7 +2,7 @@ import pytest -from textual import events +from textual import events, work from textual.app import App, ComposeResult from textual.binding import Binding from textual.containers import Center, Middle @@ -71,7 +71,7 @@ def compose(self) -> ComposeResult: pass -async def test_pilot_exception_cathing_widget_compose(): +async def test_pilot_exception_catching_widget_compose(): class SomeScreen(Screen[None]): def compose(self) -> ComposeResult: 1 / 0 @@ -101,6 +101,20 @@ def action_beep(self) -> None: await pilot.press("b") +async def test_pilot_exception_catching_worker(): + class SimpleAppThatCrashes(App[None]): + def on_mount(self) -> None: + self.crash() + + @work(name="crash") + async def crash(self) -> None: + 1 / 0 + + with pytest.raises(ZeroDivisionError): + async with SimpleAppThatCrashes().run_test(): + pass + + async def test_pilot_click_screen(): """Regression test for https://github.com/Textualize/textual/issues/3395. From 4f55ca70d188d69f3c4b7aaed03d37d411043866 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 25 Mar 2024 15:43:51 +0000 Subject: [PATCH 27/41] Change Input to not suggest right away, but to wait for an edit See #3811. --- src/textual/widgets/_input.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index 0d3149f2d3..0b3ae57ee1 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -519,6 +519,7 @@ def _on_focus(self, _: Focus) -> None: if self.cursor_blink: self._blink_timer.resume() self.app.cursor_position = self.cursor_screen_offset + self._suggestion = "" async def _on_key(self, event: events.Key) -> None: self._cursor_visible = True From 543881e5fb6a2222acb9eb47d6a4ee771f9dbc87 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 25 Mar 2024 15:54:35 +0000 Subject: [PATCH 28/41] Update the snapshot test --- .../__snapshots__/test_snapshots.ambr | 122 +++++++++--------- tests/snapshot_tests/test_snapshots.py | 2 +- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 0d054a47ee..263202293d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -20627,137 +20627,137 @@ font-weight: 700; } - .terminal-2073605363-matrix { + .terminal-2577839347-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2073605363-title { + .terminal-2577839347-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2073605363-r1 { fill: #1e1e1e } - .terminal-2073605363-r2 { fill: #0178d4 } - .terminal-2073605363-r3 { fill: #c5c8c6 } - .terminal-2073605363-r4 { fill: #e2e2e2 } - .terminal-2073605363-r5 { fill: #1e1e1e;font-style: italic; } - .terminal-2073605363-r6 { fill: #ff0000;font-style: italic; } - .terminal-2073605363-r7 { fill: #121212 } - .terminal-2073605363-r8 { fill: #e1e1e1 } + .terminal-2577839347-r1 { fill: #1e1e1e } + .terminal-2577839347-r2 { fill: #0178d4 } + .terminal-2577839347-r3 { fill: #c5c8c6 } + .terminal-2577839347-r4 { fill: #e2e2e2 } + .terminal-2577839347-r5 { fill: #1e1e1e;font-style: italic; } + .terminal-2577839347-r6 { fill: #ff0000;font-style: italic; } + .terminal-2577839347-r7 { fill: #121212 } + .terminal-2577839347-r8 { fill: #e1e1e1 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - FruitsApp + FruitsApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - strawberry - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - straw - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - p - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - b - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - a - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - - - - - + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + strawberry + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + straw + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + p + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + b + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + a + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index d33a4178f3..34a527d5e8 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -105,7 +105,7 @@ async def run_before(pilot): pilot.app.query(Input).first().cursor_blink = False assert snap_compare( - SNAPSHOT_APPS_DIR / "input_suggestions.py", press=[], run_before=run_before + SNAPSHOT_APPS_DIR / "input_suggestions.py", press=["b"], run_before=run_before ) From d55410cc0ded9f4e75294bee4f88a26c07eef16d Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Mon, 25 Mar 2024 16:45:58 +0000 Subject: [PATCH 29/41] Text area flicker fix (#4334) --- CHANGELOG.md | 1 + src/textual/widgets/_text_area.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a0cb30a51..49d8b9e2e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed a crash in `TextArea` when undoing an edit to a selection the selection was made backwards https://github.com/Textualize/textual/issues/4301 - Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 +- Fixed issue where narrow TextArea would repeatedly wrap due to scrollbar appearing/disappearing https://github.com/Textualize/textual/pull/4334 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 ### Changed diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index 501eef8f40..71e1b3fd7c 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -688,7 +688,8 @@ def _watch_indent_width(self) -> None: self.scroll_cursor_visible() def _watch_show_vertical_scrollbar(self) -> None: - self._rewrap_and_refresh_virtual_size() + if self.wrap_width: + self._rewrap_and_refresh_virtual_size() self.scroll_cursor_visible() def _watch_theme(self, theme: str) -> None: From 07b6710c5d2709df126244b90fdf5e594ef0d83b Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 26 Mar 2024 08:29:39 +0000 Subject: [PATCH 30/41] Remove trailing whitespace from the CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d8b9e2e2..e893fd24e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed issue where narrow TextArea would repeatedly wrap due to scrollbar appearing/disappearing https://github.com/Textualize/textual/pull/4334 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 -### Changed +### Changed - ProgressBar won't show ETA until there is at least one second of samples https://github.com/Textualize/textual/pull/4316 From a68698df082847ca8955bd9761c7bb9a3c10cd58 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 26 Mar 2024 08:30:47 +0000 Subject: [PATCH 31/41] Update the CGANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e893fd24e4..4645bc060f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - ProgressBar won't show ETA until there is at least one second of samples https://github.com/Textualize/textual/pull/4316 +- `Input` waits until an edit has been made, after entry to the widget, before offering a suggestion https://github.com/Textualize/textual/pull/4335 ## [0.53.1] - 2023-03-18 From 525613f8065d9ebf506efd00498900a1daeeb352 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 26 Mar 2024 08:49:14 +0000 Subject: [PATCH 32/41] Fix a typo in the docstring for the Unmount event --- src/textual/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/events.py b/src/textual/events.py index 79c7c2366a..7e5c158bc5 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -143,7 +143,7 @@ class Mount(Event, bubble=False, verbose=False): class Unmount(Event, bubble=False, verbose=False): - """Sent when a widget is unmounted and may not longer receive messages. + """Sent when a widget is unmounted and may no longer receive messages. - [ ] Bubbles - [ ] Verbose From 2299fdfc9c6d6eb54cdb89d45dece970b9182725 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 26 Mar 2024 08:55:08 +0000 Subject: [PATCH 33/41] Add the Unmount event to the docs --- docs/events/unmount.md | 3 +++ mkdocs-nav.yml | 1 + 2 files changed, 4 insertions(+) create mode 100644 docs/events/unmount.md diff --git a/docs/events/unmount.md b/docs/events/unmount.md new file mode 100644 index 0000000000..8e17c76924 --- /dev/null +++ b/docs/events/unmount.md @@ -0,0 +1,3 @@ +::: textual.events.Unmount + options: + heading_level: 1 diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index 1ba90e4ee6..7dd0863b43 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -69,6 +69,7 @@ nav: - "events/screen_resume.md" - "events/screen_suspend.md" - "events/show.md" + - "events/unmount.md" - Styles: - "styles/index.md" - "styles/align.md" From b8a1a5ceb80dae475c5c2e3e0f11538d20880fe8 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 26 Mar 2024 10:52:28 +0000 Subject: [PATCH 34/41] version bump --- CHANGELOG.md | 3 ++- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4645bc060f..bdf90d941c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## Unreleased +## [0.54.0] - 2023-03-26 ### Fixed @@ -1801,6 +1801,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040 - New handler system for messages that doesn't require inheritance - Improved traceback handling +[0.54.0]: https://github.com/Textualize/textual/compare/v0.53.1...v0.54.0 [0.53.1]: https://github.com/Textualize/textual/compare/v0.53.0...v0.53.1 [0.53.0]: https://github.com/Textualize/textual/compare/v0.52.1...v0.53.0 [0.52.1]: https://github.com/Textualize/textual/compare/v0.52.0...v0.52.1 diff --git a/pyproject.toml b/pyproject.toml index 5d8ebf16de..630cc27599 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "0.53.1" +version = "0.54.0" homepage = "https://github.com/Textualize/textual" repository = "https://github.com/Textualize/textual" documentation = "https://textual.textualize.io/" From 809f38341f35effd073d43c0625fe984f4d9e311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:56:36 +0000 Subject: [PATCH 35/41] Fix tests. --- src/textual/worker.py | 3 ++- tests/test_pilot.py | 4 +++- tests/workers/test_worker.py | 18 +++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/textual/worker.py b/src/textual/worker.py index 6cef3f6b90..f7f10b60ae 100644 --- a/src/textual/worker.py +++ b/src/textual/worker.py @@ -375,7 +375,8 @@ async def _run(self, app: App) -> None: app.log.worker(Traceback()) if self.exit_on_error: - app._handle_exception(error) + worker_failed = WorkerFailed(self._error) + app._handle_exception(worker_failed) else: self.state = WorkerState.SUCCESS app.log.worker(self) diff --git a/tests/test_pilot.py b/tests/test_pilot.py index 4a95b9c0fc..9451237fab 100644 --- a/tests/test_pilot.py +++ b/tests/test_pilot.py @@ -9,6 +9,7 @@ from textual.pilot import OutOfBounds from textual.screen import Screen from textual.widgets import Button, Label +from textual.worker import WorkerFailed KEY_CHARACTERS_TO_TEST = "akTW03" + punctuation """Test some "simple" characters (letters + digits) and all punctuation.""" @@ -110,9 +111,10 @@ def on_mount(self) -> None: async def crash(self) -> None: 1 / 0 - with pytest.raises(ZeroDivisionError): + with pytest.raises(WorkerFailed) as exc: async with SimpleAppThatCrashes().run_test(): pass + assert exc.type is ZeroDivisionError async def test_pilot_click_screen(): diff --git a/tests/workers/test_worker.py b/tests/workers/test_worker.py index 7b553d122f..61af91f638 100644 --- a/tests/workers/test_worker.py +++ b/tests/workers/test_worker.py @@ -113,10 +113,10 @@ class ErrorApp(App): pass app = ErrorApp() - async with app.run_test(): - worker: Worker[str] = Worker(app, run_error) - worker._start(app) - with pytest.raises(WorkerFailed): + with pytest.raises(WorkerFailed): + async with app.run_test(): + worker: Worker[str] = Worker(app, run_error) + worker._start(app) await worker.wait() @@ -218,12 +218,12 @@ async def self_referential_work(): await get_current_worker().wait() app = App() - async with app.run_test(): - worker = Worker(app, self_referential_work) - worker._start(app) - with pytest.raises(WorkerFailed) as exc: + with pytest.raises(WorkerFailed) as exc: + async with app.run_test(): + worker = Worker(app, self_referential_work) + worker._start(app) await worker.wait() - assert exc.type is DeadlockError + assert exc.type is DeadlockError async def test_wait_without_start(): From fe7a8998f8710ead9f228f09fd0bdddc70d141d6 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 26 Mar 2024 11:17:21 +0000 Subject: [PATCH 36/41] Add see-also relating to (un)mounting --- docs/events/load.md | 4 ++++ docs/events/mount.md | 5 +++++ docs/events/unmount.md | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/docs/events/load.md b/docs/events/load.md index 16f5e3d153..f1142f3546 100644 --- a/docs/events/load.md +++ b/docs/events/load.md @@ -1,3 +1,7 @@ ::: textual.events.Load options: heading_level: 1 + +## See also + +- [Mount](mount.md) diff --git a/docs/events/mount.md b/docs/events/mount.md index 885b8f4e9e..955391f056 100644 --- a/docs/events/mount.md +++ b/docs/events/mount.md @@ -1,3 +1,8 @@ ::: textual.events.Mount options: heading_level: 1 + +## See also + +- [Load](load.md) +- [Unmount](unmount.md) diff --git a/docs/events/unmount.md b/docs/events/unmount.md index 8e17c76924..2df6dccdd5 100644 --- a/docs/events/unmount.md +++ b/docs/events/unmount.md @@ -1,3 +1,7 @@ ::: textual.events.Unmount options: heading_level: 1 + +## See also + +- [Mount](mount.md) From fc56863f0f324e47cc9186eafa41ef2b0ea37645 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 26 Mar 2024 11:34:50 +0000 Subject: [PATCH 37/41] event docstrings --- docs/events/mouse_capture.md | 2 ++ docs/events/mouse_release.md | 2 ++ src/textual/events.py | 19 +++++++++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/events/mouse_capture.md b/docs/events/mouse_capture.md index 945da83085..953a086bf5 100644 --- a/docs/events/mouse_capture.md +++ b/docs/events/mouse_capture.md @@ -8,4 +8,6 @@ title: MouseCapture ## See also +- [capture_mouse][textual.widget.Widget.capture_mouse] +- [release_mouse][textual.widget.Widget.release_mouse] - [MouseRelease](mouse_release.md) diff --git a/docs/events/mouse_release.md b/docs/events/mouse_release.md index 438e03569b..a6e0a91cbe 100644 --- a/docs/events/mouse_release.md +++ b/docs/events/mouse_release.md @@ -8,4 +8,6 @@ title: MouseRelease ## See also +- [capture_mouse][textual.widget.Widget.capture_mouse] +- [release_mouse][textual.widget.Widget.release_mouse] - [MouseCapture](mouse_capture.md) diff --git a/src/textual/events.py b/src/textual/events.py index 7e5c158bc5..68c3ae8f3d 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -59,7 +59,7 @@ class Load(Event, bubble=False): """ Sent when the App is running but *before* the terminal is in application mode. - Use this event to run any set up that doesn't require any visuals such as loading + Use this event to run any setup that doesn't require any visuals such as loading configuration and binding keys. - [ ] Bubbles @@ -129,6 +129,9 @@ def __rich_repr__(self) -> rich.repr.Result: class Compose(Event, bubble=False, verbose=True): """Sent to a widget to request it to compose and mount children. + This event is used internally by Textual. + You won't typically need to explicitly handle it, + - [ ] Bubbles - [X] Verbose """ @@ -151,7 +154,7 @@ class Unmount(Event, bubble=False, verbose=False): class Show(Event, bubble=False): - """Sent when a widget has become visible. + """Sent when a widget is first displayed. - [ ] Bubbles - [ ] Verbose @@ -164,13 +167,17 @@ class Hide(Event, bubble=False): - [ ] Bubbles - [ ] Verbose - A widget may be hidden by setting its `visible` flag to `False`, if it is no longer in a layout, - or if it has been offset beyond the edges of the terminal. + Sent when any of the following conditions apply: + + - The widget is removed from the DOM. + - The widget is no longer displayed because it has been scrolled or clipped from the terminal or its container. + - The widget has its `display` attribute set to `False`. + - The widget's `display` style is set to `"none"`. """ class Ready(Event, bubble=False): - """Sent to the app when the DOM is ready. + """Sent to the `App` when the DOM is ready and the first frame has been displayed. - [ ] Bubbles - [ ] Verbose @@ -232,7 +239,7 @@ class Key(InputEvent): Args: key: The key that was pressed. - character: A printable character or ``None`` if it is not printable. + character: A printable character or `None` if it is not printable. """ __slots__ = ["key", "character", "aliases"] From b8f91bd016c00ee2c553ab74ea55c9bae0171dc8 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:45:35 +0000 Subject: [PATCH 38/41] docs: fix changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c1e3d9dc8..c695c34430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [0.54.0] - 2023-03-26 +## [0.54.0] - 2024-03-26 ### Fixed @@ -24,13 +24,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 -## [0.53.1] - 2023-03-18 +## [0.53.1] - 2024-03-18 ### Fixed - Fixed issue with data binding https://github.com/Textualize/textual/pull/4308 -## [0.53.0] - 2023-03-18 +## [0.53.0] - 2024-03-18 ### Added From 88a6296dcdaf0afcfa61dca9d4d62956aad9d1e9 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:55:37 +0000 Subject: [PATCH 39/41] move updates to unreleased --- CHANGELOG.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c695c34430..2a7ee5979d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased + +### Fixed + +- Exceptions inside `Widget.compose` or workers weren't bubbling up in tests https://github.com/Textualize/textual/issues/4282 + +### Added + +- Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 + ## [0.54.0] - 2024-03-26 ### Fixed @@ -13,17 +23,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 - Fixed issue where narrow TextArea would repeatedly wrap due to scrollbar appearing/disappearing https://github.com/Textualize/textual/pull/4334 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 -- Exceptions inside `Widget.compose` or workers weren't bubbling up in tests https://github.com/Textualize/textual/issues/4282 ### Changed - ProgressBar won't show ETA until there is at least one second of samples https://github.com/Textualize/textual/pull/4316 - `Input` waits until an edit has been made, after entry to the widget, before offering a suggestion https://github.com/Textualize/textual/pull/4335 -### Added - -- Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 - ## [0.53.1] - 2024-03-18 ### Fixed From 2a06e288ea318870990e975395a9a2f0b54c968e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Gr=C3=A9goire?= <32612304+valentingregoire@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:22:27 +0100 Subject: [PATCH 40/41] fix: Typo fixed. --- docs/guide/animation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/animation.md b/docs/guide/animation.md index 50bc87d35d..dde2ec7bb3 100644 --- a/docs/guide/animation.md +++ b/docs/guide/animation.md @@ -1,6 +1,6 @@ # Animation -Ths chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. +Thss chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. ## Animating styles From f05d727c01dcb62cf60b81cd23cf24e8f8a99a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Gr=C3=A9goire?= <32612304+valentingregoire@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:22:55 +0100 Subject: [PATCH 41/41] =?UTF-8?q?fix:=20Fix=20the=20fix=20to=20fix=20the?= =?UTF-8?q?=20typo=20=F0=9F=98=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide/animation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/animation.md b/docs/guide/animation.md index dde2ec7bb3..1bf55fb203 100644 --- a/docs/guide/animation.md +++ b/docs/guide/animation.md @@ -1,6 +1,6 @@ # Animation -Thss chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. +This chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. ## Animating styles