apply style to widget

This commit is contained in:
Will McGugan
2022-07-04 14:48:28 +01:00
parent 97c58a7b0a
commit f3a7c9483f
15 changed files with 701 additions and 325 deletions

25
tests/test_border.py Normal file
View File

@@ -0,0 +1,25 @@
from rich.segment import Segment
from rich.style import Style
from textual._border import get_box, render_row
def test_border_render_row():
style = Style.parse("red")
row = (Segment("", style), Segment("", style), Segment("", style))
assert render_row(row, 5, False, False) == [Segment(row[1].text * 5, row[1].style)]
assert render_row(row, 5, True, False) == [
row[0],
Segment(row[1].text * 4, row[1].style),
]
assert render_row(row, 5, False, True) == [
Segment(row[1].text * 4, row[1].style),
row[2],
]
assert render_row(row, 5, True, True) == [
row[0],
Segment(row[1].text * 3, row[1].style),
row[2],
]

View File

@@ -2,7 +2,7 @@ from rich.segment import Segment
from rich.style import Style
from textual._segment_tools import line_crop, line_trim
from textual._segment_tools import line_crop, line_trim, line_pad
def test_line_crop():
@@ -89,3 +89,25 @@ def test_line_trim():
]
assert line_trim([], True, True) == []
def test_line_pad():
segments = [Segment("foo"), Segment("bar")]
style = Style.parse("red")
assert line_pad(segments, 2, 3, style) == [
Segment(" ", style),
*segments,
Segment(" ", style),
]
assert line_pad(segments, 0, 3, style) == [
*segments,
Segment(" ", style),
]
assert line_pad(segments, 2, 0, style) == [
Segment(" ", style),
*segments,
]
assert line_pad(segments, 0, 0, style) == segments

260
tests/test_styles_cache.py Normal file
View File

@@ -0,0 +1,260 @@
from rich.segment import Segment
from textual.color import Color
from textual.geometry import Region, Size
from textual.css.styles import Styles
from textual._styles_cache import StylesCache
from textual._types import Lines
def _extract_content(lines: Lines):
"""Extract the text content from lines."""
content = ["".join(segment.text for segment in line) for line in lines]
return content
def test_set_dirty():
cache = StylesCache()
cache.set_dirty(Region(3, 4, 10, 2))
assert not cache.is_dirty(3)
assert cache.is_dirty(4)
assert cache.is_dirty(5)
assert not cache.is_dirty(6)
def test_no_styles():
"""Test that empty style returns the content un-altered"""
content = [
[Segment("foo")],
[Segment("bar")],
[Segment("baz")],
]
styles = Styles()
cache = StylesCache()
lines = cache.render(
styles,
Size(3, 3),
Color.parse("blue"),
Color.parse("green"),
content.__getitem__,
)
expected = [
[Segment("foo", styles.rich_style)],
[Segment("bar", styles.rich_style)],
[Segment("baz", styles.rich_style)],
]
assert lines == expected
def test_border():
content = [
[Segment("foo")],
[Segment("bar")],
[Segment("baz")],
]
styles = Styles()
styles.border = ("heavy", "white")
cache = StylesCache()
lines = cache.render(
styles,
Size(5, 5),
Color.parse("blue"),
Color.parse("green"),
content.__getitem__,
)
text_content = _extract_content(lines)
expected_text = [
"┏━━━┓",
"┃foo┃",
"┃bar┃",
"┃baz┃",
"┗━━━┛",
]
assert text_content == expected_text
def test_padding():
content = [
[Segment("foo")],
[Segment("bar")],
[Segment("baz")],
]
styles = Styles()
styles.padding = 1
cache = StylesCache()
lines = cache.render(
styles,
Size(5, 5),
Color.parse("blue"),
Color.parse("green"),
content.__getitem__,
)
text_content = _extract_content(lines)
expected_text = [
" ",
" foo ",
" bar ",
" baz ",
" ",
]
assert text_content == expected_text
def test_padding_border():
content = [
[Segment("foo")],
[Segment("bar")],
[Segment("baz")],
]
styles = Styles()
styles.padding = 1
styles.border = ("heavy", "white")
cache = StylesCache()
lines = cache.render(
styles,
Size(7, 7),
Color.parse("blue"),
Color.parse("green"),
content.__getitem__,
)
text_content = _extract_content(lines)
expected_text = [
"┏━━━━━┓",
"┃ ┃",
"┃ foo ┃",
"┃ bar ┃",
"┃ baz ┃",
"┃ ┃",
"┗━━━━━┛",
]
assert text_content == expected_text
def test_outline():
content = [
[Segment("foo")],
[Segment("bar")],
[Segment("baz")],
]
styles = Styles()
styles.outline = ("heavy", "white")
cache = StylesCache()
lines = cache.render(
styles,
Size(3, 3),
Color.parse("blue"),
Color.parse("green"),
content.__getitem__,
)
text_content = _extract_content(lines)
expected_text = [
"┏━┓",
"┃a┃",
"┗━┛",
]
assert text_content == expected_text
def test_crop():
content = [
[Segment("foo")],
[Segment("bar")],
[Segment("baz")],
]
styles = Styles()
styles.padding = 1
styles.border = ("heavy", "white")
cache = StylesCache()
lines = cache.render(
styles,
Size(7, 7),
Color.parse("blue"),
Color.parse("green"),
content.__getitem__,
crop=Region(2, 2, 3, 3),
)
text_content = _extract_content(lines)
expected_text = [
"foo",
"bar",
"baz",
]
assert text_content == expected_text
def test_dirty_cache():
"""Check that we only render content once or if it has been marked as dirty."""
content = [
[Segment("foo")],
[Segment("bar")],
[Segment("baz")],
]
rendered_lines: list[int] = []
def get_content_line(y: int) -> list[Segment]:
rendered_lines.append(y)
return content[y]
styles = Styles()
styles.padding = 1
styles.border = ("heavy", "white")
cache = StylesCache()
lines = cache.render(
styles,
Size(7, 7),
Color.parse("blue"),
Color.parse("green"),
get_content_line,
)
assert rendered_lines == [0, 1, 2]
del rendered_lines[:]
text_content = _extract_content(lines)
expected_text = [
"┏━━━━━┓",
"┃ ┃",
"┃ foo ┃",
"┃ bar ┃",
"┃ baz ┃",
"┃ ┃",
"┗━━━━━┛",
]
assert text_content == expected_text
# Re-render styles, check that content was not requested
lines = cache.render(
styles,
Size(7, 7),
Color.parse("blue"),
Color.parse("green"),
get_content_line,
)
assert rendered_lines == []
del rendered_lines[:]
text_content = _extract_content(lines)
assert text_content == expected_text
# Mark 2 lines as dirty
cache.set_dirty(Region(0, 2, 7, 2))
lines = cache.render(
styles,
Size(7, 7),
Color.parse("blue"),
Color.parse("green"),
get_content_line,
)
assert rendered_lines == [0, 1]
text_content = _extract_content(lines)
assert text_content == expected_text