Skip to content

Commit ed79ffd

Browse files
committed
fix percentages
1 parent 284ced4 commit ed79ffd

File tree

9 files changed

+57
-23
lines changed

9 files changed

+57
-23
lines changed

src/textual/_compositor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ def get_style_at(self, x: int, y: int) -> Style:
860860
x -= region.x
861861
y -= region.y
862862

863+
# TODO: This prompts a render, can we avoid that?
863864
visible_screen_stack.set(widget.app._background_screens)
864865
lines = widget.render_lines(Region(0, y, region.width, 1))
865866

src/textual/_style_parse.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from textual.color import TRANSPARENT, Color
1+
from textual._context import active_app
2+
from textual.color import Color
23
from textual.css.parse import substitute_references
34
from textual.css.tokenize import tokenize_style, tokenize_values
45
from textual.style import Style
@@ -13,8 +14,11 @@
1314
"s": "strike",
1415
}
1516

17+
from textual._profile import timer
1618

17-
def style_parse(style_text: str, variables: dict[str, str]) -> Style:
19+
20+
@timer("style parse")
21+
def style_parse(style_text: str, variables: dict[str, str] | None) -> Style:
1822
styles: dict[str, bool | None] = {style: None for style in STYLES}
1923

2024
color: Color | None = None
@@ -25,19 +29,27 @@ def style_parse(style_text: str, variables: dict[str, str]) -> Style:
2529
data: dict[str, str] = {}
2630
meta: dict[str, object] = {}
2731

28-
reference_tokens = tokenize_values(variables)
32+
if variables is None:
33+
try:
34+
app = active_app.get()
35+
except LookupError:
36+
pass
37+
else:
38+
reference_tokens = app.stylesheet._variable_tokens
39+
else:
40+
reference_tokens = tokenize_values(variables)
2941

3042
for token in substitute_references(
3143
tokenize_style(style_text, read_from=("inline style", "")), reference_tokens
3244
):
45+
3346
name = token.name
3447
value = token.value
3548

3649
if name == "color":
3750
token_color = Color.parse(value)
3851
if is_background:
3952
background = token_color
40-
is_background = False
4153
else:
4254
color = token_color
4355
elif name == "token":
@@ -72,14 +84,14 @@ def style_parse(style_text: str, variables: dict[str, str]) -> Style:
7284
meta[key[1:]] = value
7385
else:
7486
data[key] = value
75-
elif name == "percent":
76-
percent = int(value.rstrip("%")) / 100
77-
if background is None:
78-
background = TRANSPARENT
87+
elif name == "percent" or (name == "scalar" and value.endswith("%")):
88+
percent = int(value.rstrip("%")) / 100.0
7989
if is_background:
80-
background = background.multiply_alpha(percent)
90+
if background is not None:
91+
background = background.multiply_alpha(percent)
8192
else:
82-
color = background.multiply_alpha(percent)
93+
if color is not None:
94+
color = color.multiply_alpha(percent)
8395

8496
parsed_style = Style(background, color, link=data.get("link", None), **styles)
8597
if meta:

src/textual/color.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,8 @@ def multiply_alpha(self, alpha: float) -> Color:
382382
"""
383383
if self.ansi is not None:
384384
return self
385-
r, g, b, a, _, _ = self
386-
return Color(r, g, b, a * alpha)
385+
r, g, b, a, _ansi, auto = self
386+
return Color(r, g, b, a * alpha, auto=auto)
387387

388388
@lru_cache(maxsize=1024)
389389
def blend(

src/textual/content.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -845,18 +845,11 @@ def render(
845845
return
846846

847847
if parse_style is None:
848-
try:
849-
app = active_app.get()
850-
except LookupError:
851-
css_variables = {}
852-
else:
853-
css_variables = app.get_css_variables()
854-
# TODO: Update when we add Content.from_markup
855848

856849
@lru_cache(maxsize=1024)
857850
def get_style(style: str, /) -> Style:
858851
try:
859-
visual_style = Style.parse(style, css_variables)
852+
visual_style = Style.parse(style)
860853
except Exception:
861854
visual_style = Style.null()
862855
return visual_style

src/textual/css/tokenize.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ class StyleTokenizerState(TokenizerState):
251251
key_value=r"[@a-zA-Z_-][a-zA-Z0-9_-]*=.*",
252252
key_value_quote=r"[@a-zA-Z_-][a-zA-Z0-9_-]*='.*'",
253253
key_value_double_quote=r"""[@a-zA-Z_-][a-zA-Z0-9_-]*=".*\"""",
254-
percent=PERCENT,
255254
color=COLOR,
256255
token=TOKEN,
257256
variable_ref=VARIABLE_REF,

src/textual/markup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class MarkupError(Exception):
2929
__all__ = ["MarkupError", "escape", "to_content"]
3030

3131
RE_TAGS = re.compile(
32-
r"""((\\*)\[([a-z#/@][^[]*?)])""",
32+
r"""((\\*)\[([\$a-z#/@][^[]*?)])""",
3333
re.VERBOSE,
3434
)
3535

src/textual/style.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def null(cls) -> Style:
120120
def parse(cls, text_style: str, variables: dict[str, str] | None = None) -> Style:
121121
from textual._style_parse import style_parse
122122

123-
return style_parse(text_style, {} if variables is None else variables)
123+
return style_parse(text_style, variables)
124124

125125
@classmethod
126126
def from_rich_style(
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from textual.app import App, ComposeResult
2+
from textual.widgets import Label
3+
4+
5+
class ContentApp(App):
6+
7+
def compose(self) -> ComposeResult:
8+
yield Label("[bold]Bold[/] [italic]Italic[/] [u]Underline[/] [s]Strike[/s]")
9+
yield Label(
10+
"[$primary]Primary[/] [$secondary]Secondary[/] [$warning]Warning[/] [$error]Error[/]"
11+
)
12+
yield Label("[$text on $primary]Text on Primary")
13+
yield Label("[$primary on $primary-muted]Primary on primary muted")
14+
yield Label("[$error on $error-muted]Error on error muted")
15+
yield Label(
16+
"[on $boost] [on $boost] [on $boost] Three layers of $boost [/] [/] [/]"
17+
)
18+
yield Label("[on $primary 20%]On primary twenty percent")
19+
yield Label("[$text 80% on primary]Hello")
20+
21+
22+
if __name__ == "__main__":
23+
app = ContentApp()
24+
app.run()

tests/snapshot_tests/test_snapshots.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3356,3 +3356,8 @@ async def run_before(pilot: Pilot) -> None:
33563356
await pilot.pause()
33573357

33583358
assert snap_compare(LApp(), run_before=run_before)
3359+
3360+
3361+
def test_markup(snap_compare):
3362+
"""Check markup rendering, text in test should match the markup."""
3363+
assert snap_compare(SNAPSHOT_APPS_DIR / "markup.py")

0 commit comments

Comments
 (0)