11from __future__ import annotations
22
33from functools import lru_cache
4- from sys import intern
54from typing import TYPE_CHECKING , Callable , Iterable , Sequence
65
76import rich .repr
1413from textual ._border import get_box , render_border_label , render_row
1514from textual ._context import active_app
1615from textual ._opacity import _apply_opacity
17- from textual ._segment_tools import apply_hatch , line_pad , line_trim
16+ from textual ._segment_tools import apply_hatch , line_pad , line_trim , make_blank
1817from textual .color import TRANSPARENT , Color
1918from textual .constants import DEBUG
2019from textual .content import Content
3433RenderLineCallback : TypeAlias = Callable [[int ], Strip ]
3534
3635
37- @lru_cache (1024 * 8 )
38- def make_blank (width , style : RichStyle ) -> Segment :
39- """Make a blank segment.
40-
41- Args:
42- width: Width of blank.
43- style: Style of blank.
44-
45- Returns:
46- A single segment
47- """
48- return Segment (intern (" " * width ), style )
49-
50-
5136@rich .repr .auto (angular = True )
5237class StylesCache :
5338 """Responsible for rendering CSS Styles and keeping a cache of rendered lines.
@@ -105,6 +90,7 @@ def is_dirty(self, y: int) -> bool:
10590
10691 def clear (self ) -> None :
10792 """Clear the styles cache (will cause the content to re-render)."""
93+
10894 self ._cache .clear ()
10995 self ._dirty_lines .clear ()
11096
@@ -122,14 +108,15 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]:
122108 border_title = widget ._border_title
123109 border_subtitle = widget ._border_subtitle
124110
125- base_background , background = widget ._opacity_background_colors
111+ base_background , background = widget .background_colors
126112 styles = widget .styles
127113 strips = self .render (
128114 styles ,
129115 widget .region .size ,
130116 base_background ,
131117 background ,
132118 widget .render_line ,
119+ widget .get_line_filters (),
133120 (
134121 None
135122 if border_title is None
@@ -149,7 +136,6 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]:
149136 content_size = widget .content_region .size ,
150137 padding = styles .padding ,
151138 crop = crop ,
152- filters = widget .app ._filters ,
153139 opacity = widget .opacity ,
154140 ansi_theme = widget .app .ansi_theme ,
155141 )
@@ -177,12 +163,12 @@ def render(
177163 base_background : Color ,
178164 background : Color ,
179165 render_content_line : RenderLineCallback ,
166+ filters : Sequence [LineFilter ],
180167 border_title : tuple [Content , Color , Color , Style ] | None ,
181168 border_subtitle : tuple [Content , Color , Color , Style ] | None ,
182169 content_size : Size | None = None ,
183170 padding : Spacing | None = None ,
184171 crop : Region | None = None ,
185- filters : Sequence [LineFilter ] | None = None ,
186172 opacity : float = 1.0 ,
187173 ansi_theme : TerminalTheme = DEFAULT_TERMINAL_THEME ,
188174 ) -> list [Strip ]:
@@ -223,9 +209,7 @@ def render(
223209
224210 is_dirty = self ._dirty_lines .__contains__
225211 render_line = self .render_line
226- apply_filters = (
227- [] if filters is None else [filter for filter in filters if filter .enabled ]
228- )
212+
229213 for y in crop .line_range :
230214 if is_dirty (y ) or y not in self ._cache :
231215 strip = render_line (
@@ -246,7 +230,7 @@ def render(
246230 else :
247231 strip = self ._cache [y ]
248232
249- for filter in apply_filters :
233+ for filter in filters :
250234 strip = strip .apply_filter (filter , background )
251235
252236 if DEBUG :
@@ -263,6 +247,16 @@ def render(
263247
264248 return strips
265249
250+ @lru_cache (1024 )
251+ def get_inner_outer (
252+ cls , base_background : Color , background : Color
253+ ) -> tuple [Style , Style ]:
254+ """Get inner and outer background colors."""
255+ return (
256+ Style (background = base_background + background ),
257+ Style (background = base_background ),
258+ )
259+
266260 def render_line (
267261 self ,
268262 styles : StylesBase ,
@@ -319,9 +313,7 @@ def render_line(
319313 ) = styles .outline
320314
321315 from_color = RichStyle .from_color
322-
323- inner = Style (background = (base_background + background ))
324- outer = Style (background = base_background )
316+ inner , outer = self .get_inner_outer (base_background , background )
325317
326318 def line_post (segments : Iterable [Segment ]) -> Iterable [Segment ]:
327319 """Apply effects to segments inside the border."""
@@ -343,7 +335,6 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
343335 Returns:
344336 New list of segments
345337 """
346-
347338 try :
348339 app = active_app .get ()
349340 ansi_theme = app .ansi_theme
@@ -361,7 +352,6 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
361352 line : Iterable [Segment ]
362353 # Draw top or bottom borders (A)
363354 if (border_top and y == 0 ) or (border_bottom and y == height - 1 ):
364-
365355 is_top = y == 0
366356 border_color = base_background + (
367357 border_top_color if is_top else border_bottom_color
@@ -427,7 +417,7 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
427417 elif (pad_top and y < gutter .top ) or (
428418 pad_bottom and y >= height - gutter .bottom
429419 ):
430- background_rich_style = from_color ( bgcolor = background . rich_color )
420+ background_rich_style = inner . rich_style
431421 left_style = Style (
432422 foreground = base_background + border_left_color .multiply_alpha (opacity )
433423 )
@@ -450,15 +440,12 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
450440 content_y = y - gutter .top
451441 if content_y < content_height :
452442 line = render_content_line (y - gutter .top )
453- line = line .adjust_cell_length (content_width )
443+ line = line .adjust_cell_length (content_width , inner . rich_style )
454444 else :
455- line = [make_blank (content_width , inner .rich_style )]
456- if inner :
457- line = Segment .apply_style (line , inner .rich_style )
458- if styles .text_opacity != 1.0 :
459- line = TextOpacity .process_segments (
460- line , styles .text_opacity , ansi_theme
461- )
445+ line = Strip .blank (content_width , inner .rich_style )
446+
447+ if (text_opacity := styles .text_opacity ) != 1.0 :
448+ line = TextOpacity .process_segments (line , text_opacity , ansi_theme )
462449 line = line_post (line_pad (line , pad_left , pad_right , inner .rich_style ))
463450
464451 if border_left or border_right :
0 commit comments