height cache

This commit is contained in:
Will McGugan
2025-03-24 15:17:43 +00:00
parent 47d3f04f2b
commit 1396b0f1a0
2 changed files with 26 additions and 14 deletions

View File

@@ -138,6 +138,7 @@ class Content(Visual):
self._spans: list[Span] = [] if spans is None else spans
self._cell_length = cell_length
self._optimal_width_cache: int | None = None
self._height_cache: tuple[tuple[int, str, bool] | None, int] = (None, 0)
def __str__(self) -> str:
return self._text
@@ -450,13 +451,20 @@ class Content(Visual):
Returns:
A height in lines.
"""
line_pad = rules.get("line_pad", 0) * 2
lines = self.without_spans._wrap_and_format(
width - line_pad,
overflow=rules.get("text_overflow", "fold"),
no_wrap=rules.get("text_wrap", "wrap") == "nowrap",
)
return len(lines)
get_rule = rules.get
line_pad = get_rule("line_pad", 0) * 2
overflow = get_rule("text_overflow", "fold")
no_wrap = get_rule("text_wrap", "wrap") == "nowrap"
cache_key = (width + line_pad, overflow, no_wrap)
if self._height_cache[0] == cache_key:
height = self._height_cache[1]
else:
lines = self.without_spans._wrap_and_format(
width - line_pad, overflow=overflow, no_wrap=no_wrap
)
height = len(lines)
self._height_cache = (cache_key, height)
return height
def _wrap_and_format(
self,
@@ -518,9 +526,14 @@ class Content(Visual):
line.plain, width - line_pad * 2, fold=overflow == "fold"
)
divided_lines = content_line.content.divide(offsets)
ellipsis = overflow == "ellipsis"
divided_lines = [
line.rstrip().truncate(width, ellipsis=overflow == "ellipsis")
for line in divided_lines
(
line.truncate(width, ellipsis=ellipsis)
if last
else line.rstrip().truncate(width, ellipsis=ellipsis)
)
for last, line in loop_last(divided_lines)
]
new_lines = [

View File

@@ -9,7 +9,6 @@ changed or wrapped in some way.
from __future__ import annotations
import pytest
from rich.text import Text
from textual.app import App, ComposeResult
from textual.widgets import SelectionList
@@ -44,16 +43,16 @@ async def test_get_selection_by_index() -> None:
async with SelectionListApp().run_test() as pilot:
option_list = pilot.app.query_one(SelectionList)
for n in range(5):
assert option_list.get_option_at_index(n).prompt == Text(str(n))
assert option_list.get_option_at_index(-1).prompt == Text("4")
assert str(option_list.get_option_at_index(n).prompt) == str(n)
assert str(option_list.get_option_at_index(-1).prompt) == "4"
async def test_get_selection_by_id() -> None:
"""It should be possible to get a selection by ID."""
async with SelectionListApp().run_test() as pilot:
option_list = pilot.app.query_one(SelectionList)
assert option_list.get_option("3").prompt == Text("3")
assert option_list.get_option("4").prompt == Text("4")
assert str(option_list.get_option("3").prompt) == "3"
assert str(option_list.get_option("4").prompt) == "4"
async def test_add_later() -> None: