From 98afc2214b8ab2f3cdf995d8f1da32d44c763c35 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Sat, 19 Jul 2025 14:21:54 +0100 Subject: [PATCH 1/3] fix(text area): fix stuck background with `css` theme Fix `TextArea` issue with the `css` theme, where the background color was stuck from the previous theme. Fixes #5964 --- src/textual/widgets/_text_area.py | 8 + ...text_area_css_theme_updates_background.svg | 153 ++++++++++++++++++ ...t_text_area_read_only_cursor_rendering.svg | 42 ++--- .../test_text_area_themes[css].svg | 76 ++++----- tests/snapshot_tests/test_snapshots.py | 26 +++ 5 files changed, 246 insertions(+), 59 deletions(-) create mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index 771f441581..a7db8c1d7b 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -860,6 +860,14 @@ def _set_theme(self, theme: str) -> None: self.styles.color = Color.from_rich_color(color) if background: self.styles.background = Color.from_rich_color(background) + else: + # When the theme doesn't define a base style (e.g. the `css` theme), + # the TextArea background/color should fallback to its CSS colors. + # + # Since these styles may have already been changed by another theme, + # we need to reset the background/color styles to the default values. + self.styles.color = None + self.styles.background = None @property def available_themes(self) -> set[str]: diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg new file mode 100644 index 0000000000..cc32704154 --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TextAreaThemeApp + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +This TextArea theme is always `css`.                                         + + + + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +This TextArea theme changes from `github_light` to `css`.                    +The colors should match the TextArea above.                                  + + + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg index b1c1b9b98a..86db6b1030 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg @@ -19,59 +19,59 @@ font-weight: 700; } - .terminal-2506374631-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2506374631-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2506374631-r1 { fill: #121212 } -.terminal-2506374631-r2 { fill: #0178d4 } -.terminal-2506374631-r3 { fill: #c5c8c6 } -.terminal-2506374631-r4 { fill: #999997;font-weight: bold } -.terminal-2506374631-r5 { fill: #f8f8f2 } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #959595;font-weight: bold } +.terminal-r5 { fill: #e0e0e0 } - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -1  Hello, world!           - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +1  Hello, world!           + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg index 0d6b98a198..9e850d91d4 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg @@ -19,84 +19,84 @@ font-weight: 700; } - .terminal-1378439235-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1378439235-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1378439235-r1 { fill: #121212 } -.terminal-1378439235-r2 { fill: #0178d4 } -.terminal-1378439235-r3 { fill: #c5c8c6 } -.terminal-1378439235-r4 { fill: #71716e } -.terminal-1378439235-r5 { fill: #569cd6 } -.terminal-1378439235-r6 { fill: #f8f8f2 } -.terminal-1378439235-r7 { fill: #dcdcaa } -.terminal-1378439235-r8 { fill: #cccccc } -.terminal-1378439235-r9 { fill: #9cdcfe } -.terminal-1378439235-r10 { fill: #999997;font-weight: bold } -.terminal-1378439235-r11 { fill: #b5cea8 } -.terminal-1378439235-r12 { fill: #7daf9c } -.terminal-1378439235-r13 { fill: #4ec9b0 } -.terminal-1378439235-r14 { fill: #ce9178 } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #6b6b6b } +.terminal-r5 { fill: #569cd6 } +.terminal-r6 { fill: #e0e0e0 } +.terminal-r7 { fill: #dcdcaa } +.terminal-r8 { fill: #cccccc } +.terminal-r9 { fill: #9cdcfe } +.terminal-r10 { fill: #959595;font-weight: bold } +.terminal-r11 { fill: #b5cea8 } +.terminal-r12 { fill: #7daf9c } +.terminal-r13 { fill: #4ec9b0 } +.terminal-r14 { fill: #ce9178 } - + - + - + - + - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -1  defhello(name): -2  x=123 -3  whilenotFalse: -4  print("hello "+name) -5  continue -6   - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +1  defhello(name): +2  x=123 +3  whilenotFalse: +4  print("hello "+name) +5  continue +6   + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 4ffcec995d..7a41f0009d 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -4421,3 +4421,29 @@ async def on_mount(self) -> None: await asyncio.sleep(0.01) assert snap_compare(MDApp(), press=["1", "wait:500"]) + + +def test_text_area_css_theme_updates_background(snap_compare): + """Regression test for https://github.com/Textualize/textual/issues/5964""" + + class TextAreaThemeApp(App): + def compose(self) -> ComposeResult: + yield TextArea( + "This TextArea theme is always `css`.", + theme="css", + show_cursor=False, + id="text-area-control", + ) + yield TextArea( + "This TextArea theme changes from `github_light` to `css`.\n" + "The colors should match the TextArea above.", + theme="github_light", + show_cursor=False, + id="text-area-variable", + ) + + def on_mount(self) -> None: + text_area = self.query_one("#text-area-variable", TextArea) + text_area.theme = "css" + + assert snap_compare(TextAreaThemeApp()) From ba10e2baca2eda59e37fbeb6ad85e2803792c68c Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Mon, 21 Jul 2025 20:29:59 +0100 Subject: [PATCH 2/3] docs(changelog): add fix for text area issue with css theme --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d6859dc5..ce2a9fd72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `index` to `ListView.Selected` event https://github.com/Textualize/textual/pull/5973 - Added `layout` switch to Static.update https://github.com/Textualize/textual/pull/5973 +### Fixed + +- Fixed `TextArea` issue with the `css` theme, where the background color was stuck from the previous theme https://github.com/Textualize/textual/issues/5964 + ### Changed - Improved rendering of Markdown tables (replace Rich table with grid) which allows text selection https://github.com/Textualize/textual/pull/5962 From c94cc6718ec4e26847de25bdd6c669c1b327295a Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Mon, 21 Jul 2025 21:04:14 +0100 Subject: [PATCH 3/3] test(snapshots): prevent flaky text area snapshot test --- tests/snapshot_tests/test_snapshots.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 7a41f0009d..6bde2bdd17 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -4428,19 +4428,23 @@ def test_text_area_css_theme_updates_background(snap_compare): class TextAreaThemeApp(App): def compose(self) -> ComposeResult: - yield TextArea( + text_area_control = TextArea( "This TextArea theme is always `css`.", theme="css", - show_cursor=False, id="text-area-control", ) - yield TextArea( + text_area_control.cursor_blink = False + + text_area_variable = TextArea( "This TextArea theme changes from `github_light` to `css`.\n" "The colors should match the TextArea above.", theme="github_light", - show_cursor=False, id="text-area-variable", ) + text_area_variable.cursor_blink = False + + yield text_area_control + yield text_area_variable def on_mount(self) -> None: text_area = self.query_one("#text-area-variable", TextArea)