diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e6f13bafd..1ca8138dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ 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.65.1] - 2024-06-05 + +### Fixed + +- Fixed hot reloading with hatch rule https://github.com/Textualize/textual/pull/4606 +- Fixed hatch style parsing https://github.com/Textualize/textual/pull/4606 + ## [0.65.0] - 2024-06-05 ### Added @@ -2062,6 +2069,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.65.1]: https://github.com/Textualize/textual/compare/v0.65.0...v0.65.1 [0.65.0]: https://github.com/Textualize/textual/compare/v0.64.0...v0.65.0 [0.64.0]: https://github.com/Textualize/textual/compare/v0.63.6...v0.64.0 [0.63.6]: https://github.com/Textualize/textual/compare/v0.63.5...v0.63.6 diff --git a/docs/examples/styles/hatch.tcss b/docs/examples/styles/hatch.tcss index 989d35efaa..b2bcbce119 100644 --- a/docs/examples/styles/hatch.tcss +++ b/docs/examples/styles/hatch.tcss @@ -1,6 +1,7 @@ .hatch { height: 1fr; border: solid $secondary; + &.cross { hatch: cross $success; } diff --git a/pyproject.toml b/pyproject.toml index 6f66f4609d..cdc7e92414 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "0.65.0" +version = "0.65.1" homepage = "https://github.com/Textualize/textual" repository = "https://github.com/Textualize/textual" documentation = "https://textual.textualize.io/" diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py index c21c3b9e14..361cdfae40 100644 --- a/src/textual/css/_style_properties.py +++ b/src/textual/css/_style_properties.py @@ -1148,7 +1148,11 @@ class HatchProperty: def __get__(self, obj: StylesBase, type: type[StylesBase]) -> tuple[str, Color]: return cast("tuple[str, Color]", obj.get_rule("hatch", (" ", TRANSPARENT))) - def __set__(self, obj: StylesBase, value: tuple[str, Color | str]) -> None: + def __set__(self, obj: StylesBase, value: tuple[str, Color | str] | None) -> None: + _rich_traceback_omit = True + if value is None: + obj.clear_rule("hatch") + return character, color = value if len(character) != 1: try: diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index 760024cd52..622312a861 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -1058,51 +1058,73 @@ def process_constrain(self, name: str, tokens: list[Token]) -> None: self.styles._rules[name] = value # type: ignore def process_hatch(self, name: str, tokens: list[Token]) -> None: - character = " " + if not tokens: + return + character: str | None = None color = TRANSPARENT opacity = 1.0 - for token in tokens: - if token.name == "token": - if token.value not in VALID_HATCH: - self.error( - name, - tokens[0], - string_enum_help_text(name, VALID_HATCH, context="css"), - ) - character = HATCHES[token.value] - elif token.name == "string": - character = token.value[1:-1] - if len(character) != 1: - self.error( - name, - token, - f"Hatch requires a string of length 1; got {token.value}", - ) - if cell_len(character) != 1: - self.error( - name, - token, - f"Hatch requires a string with a *cell length* of 1; got {token.value}", - ) - elif token.name == "color": - try: - color = Color.parse(token.value) - except Exception as error: - self.error( - name, - token, - color_property_help_text(name, context="css", error=error), - ) - elif token.name == "scalar": - opacity_scalar = opacity = Scalar.parse(token.value) + if len(tokens) not in (2, 3): + self.error(name, tokens[0], "2 or 3 values expected here") + + character_token, color_token, *opacity_tokens = tokens + + if character_token.name == "token": + if character_token.value not in VALID_HATCH: + self.error( + name, + tokens[0], + string_enum_help_text(name, VALID_HATCH, context="css"), + ) + character = HATCHES[character_token.value] + elif character_token.name == "string": + character = character_token.value[1:-1] + if len(character) != 1: + self.error( + name, + character_token, + f"Hatch type requires a string of length 1; got {character_token.value}", + ) + if cell_len(character) != 1: + self.error( + name, + character_token, + f"Hatch type requires a string with a *cell length* of 1; got {character_token.value}", + ) + + if color_token.name in ("color", "token"): + try: + color = Color.parse(color_token.value) + except Exception as error: + self.error( + name, + color_token, + color_property_help_text(name, context="css", error=error), + ) + else: + self.error( + name, color_token, f"Expected a color; found {color_token.value!r}" + ) + + if opacity_tokens: + opacity_token = opacity_tokens[0] + if opacity_token.name == "scalar": + opacity_scalar = opacity = Scalar.parse(opacity_token.value) if opacity_scalar.unit != Unit.PERCENT: self.error( - name, token, "hatch alpha must be given as a percentage." + name, + opacity_token, + "hatch alpha must be given as a percentage.", ) opacity = clamp(opacity_scalar.value / 100.0, 0, 1.0) + else: + self.error( + name, + opacity_token, + f"expected a percentage here; found {opacity_token.value!r}", + ) - self.styles._rules[name] = (character, color.multiply_alpha(opacity)) + self.styles._rules[name] = (character or " ", color.multiply_alpha(opacity)) def _get_suggested_property_name_for_rule(self, rule_name: str) -> str | None: """ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 9499dc7aab..b53d32da31 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -22282,138 +22282,139 @@ font-weight: 700; } - .terminal-3393210531-matrix { + .terminal-2628811343-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3393210531-title { + .terminal-2628811343-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3393210531-r1 { fill: #004578 } - .terminal-3393210531-r2 { fill: #c5c8c6 } - .terminal-3393210531-r3 { fill: #4ebf71 } - .terminal-3393210531-r4 { fill: #fea62b } - .terminal-3393210531-r5 { fill: #b93c5b } - .terminal-3393210531-r6 { fill: #ff0000 } - .terminal-3393210531-r7 { fill: #366e47 } - .terminal-3393210531-r8 { fill: #ff00ff;font-weight: bold } + .terminal-2628811343-r1 { fill: #6a5acd } + .terminal-2628811343-r2 { fill: #c5c8c6 } + .terminal-2628811343-r3 { fill: #4ebf71 } + .terminal-2628811343-r4 { fill: #fea62b } + .terminal-2628811343-r5 { fill: #b93c5b } + .terminal-2628811343-r6 { fill: #ff0000 } + .terminal-2628811343-r7 { fill: #004578 } + .terminal-2628811343-r8 { fill: #366e47 } + .terminal-2628811343-r9 { fill: #ff00ff;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - HatchApp + HatchApp - + - - ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ - ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ - ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳────────────────────────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳── Hello World ──────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼Hatched┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳──────────────────────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳────────────────────────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ - ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ - ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ - ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ + + ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ + ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ + ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳────────────────────────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳── Hello World ──────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼Hatched┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──││││││││││││││││││││││││││││││││││││││││││││││││││──╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳──────────────────────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳────────────────────────────────────────────────────────╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ + ╱╱╱╱╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╱╱╱╱ + ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ + ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ diff --git a/tests/snapshot_tests/snapshot_apps/hatch.py b/tests/snapshot_tests/snapshot_apps/hatch.py index 12c1c3a7b2..19859fd35f 100644 --- a/tests/snapshot_tests/snapshot_apps/hatch.py +++ b/tests/snapshot_tests/snapshot_apps/hatch.py @@ -6,7 +6,7 @@ class HatchApp(App): CSS = """ Screen { - hatch: right $primary; + hatch: right slateblue; } #one { hatch: left $success;