Skip to content

Commit c40c265

Browse files
committed
tint fix
1 parent 4a822bc commit c40c265

File tree

4 files changed

+72
-11
lines changed

4 files changed

+72
-11
lines changed

src/textual/color.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,31 @@ def blend(
399399
new_alpha,
400400
)
401401

402+
@lru_cache(maxsize=1024)
403+
def tint(self, color: Color) -> Color:
404+
"""Apply a tint to a color.
405+
406+
Similar to blend, but combines color and alpha.
407+
408+
Args:
409+
color: A color with alpha component.
410+
411+
Returns:
412+
New color
413+
"""
414+
r2, g2, b2, a2, ansi2 = color
415+
if ansi2 is not None:
416+
return color
417+
r1, g1, b1, a1, ansi1 = self
418+
if ansi1 is not None:
419+
return color
420+
return Color(
421+
int(r1 + (r2 - r1) * a2),
422+
int(g1 + (g2 - g1) * a2),
423+
int(b1 + (b2 - b1) * a2),
424+
a1,
425+
)
426+
402427
def __add__(self, other: object) -> Color:
403428
if isinstance(other, Color):
404429
return self.blend(other, other.a, 1.0)

src/textual/dom.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,11 +1026,11 @@ def rich_style(self) -> Style:
10261026
has_rule = styles.has_rule
10271027
opacity *= styles.opacity
10281028
if has_rule("background"):
1029-
text_background = (
1030-
background + styles.background + styles.background_tint
1029+
text_background = background + styles.background.tint(
1030+
styles.background_tint
10311031
)
10321032
background += (
1033-
styles.background + styles.background_tint
1033+
styles.background.tint(styles.background_tint)
10341034
).multiply_alpha(opacity)
10351035
else:
10361036
text_background = background
@@ -1119,7 +1119,7 @@ def background_colors(self) -> tuple[Color, Color]:
11191119
for node in reversed(self.ancestors_with_self):
11201120
styles = node.styles
11211121
base_background = background
1122-
background += styles.background + styles.background_tint
1122+
background += styles.background.tint(styles.background_tint)
11231123
return (base_background, background)
11241124

11251125
@property
@@ -1135,7 +1135,7 @@ def _opacity_background_colors(self) -> tuple[Color, Color]:
11351135
styles = node.styles
11361136
base_background = background
11371137
opacity *= styles.opacity
1138-
background += (styles.background + styles.background_tint).multiply_alpha(
1138+
background += styles.background.tint(styles.background_tint).multiply_alpha(
11391139
opacity
11401140
)
11411141
return (base_background, background)
@@ -1152,7 +1152,7 @@ def colors(self) -> tuple[Color, Color, Color, Color]:
11521152
for node in reversed(self.ancestors_with_self):
11531153
styles = node.styles
11541154
base_background = background
1155-
background += styles.background + styles.background_tint
1155+
background += styles.background.tint(styles.background_tint)
11561156
if styles.has_rule("color"):
11571157
base_color = color
11581158
if styles.auto_color:

tests/snapshot_tests/test_snapshots.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2314,15 +2314,27 @@ def compose(self) -> ComposeResult:
23142314

23152315

23162316
def test_background_tint(snap_compare):
2317+
"""Test background tint with alpha."""
2318+
2319+
# The screen background is dark blue
2320+
# The vertical is 20% white
2321+
# With no background tint, the verticals will be a light blue
2322+
# With a 100% tint, the vertical should be 20% red plus the blue (i.e. purple)
2323+
2324+
# tl;dr you should see 4 bars, blue at the top, purple at the bottom, and two shades in betweenm
2325+
23172326
class BackgroundTintApp(App):
23182327
CSS = """
2328+
Screen {
2329+
background: rgb(0,0,100)
2330+
}
23192331
Vertical {
2320-
background: $panel;
2332+
background: rgba(255,255,255,0.2);
23212333
}
2322-
#tint1 { background-tint: $foreground 0%; }
2323-
#tint2 { background-tint: $foreground 33%; }
2324-
#tint3 { background-tint: $foreground 66%; }
2325-
#tint4 { background-tint: $foreground 100% }
2334+
#tint1 { background-tint: rgb(255,0,0) 0%; }
2335+
#tint2 { background-tint: rgb(255,0,0) 33%; }
2336+
#tint3 { background-tint: rgb(255,0,0) 66%; }
2337+
#tint4 { background-tint: rgb(255,0,0) 100% }
23262338
"""
23272339

23282340
def compose(self) -> ComposeResult:

tests/test_color.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,27 @@ def test_is_transparent():
265265
assert not Color(20, 20, 30, a=0.01).is_transparent
266266
assert not Color(20, 20, 30, a=1).is_transparent
267267
assert not Color(20, 20, 30, 0, ansi=1).is_transparent
268+
269+
270+
@pytest.mark.parametrize(
271+
"base,tint,expected",
272+
[
273+
(
274+
Color(0, 0, 0),
275+
Color(10, 20, 30),
276+
Color(10, 20, 30),
277+
),
278+
(
279+
Color(0, 0, 0, 0.5),
280+
Color(255, 255, 255, 0.5),
281+
Color(127, 127, 127, 0.5),
282+
),
283+
(
284+
Color(100, 0, 0, 0.2),
285+
Color(0, 100, 0, 0.5),
286+
Color(50, 50, 0, 0.2),
287+
),
288+
],
289+
)
290+
def test_tint(base: Color, tint: Color, expected: Color) -> None:
291+
assert base.tint(tint) == expected

0 commit comments

Comments
 (0)