Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions src/textual/_compositor.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def render_segments(self, console: Console) -> str:
class LayoutUpdate(CompositorUpdate):
"""A renderable containing the result of a render for a given region."""

def __init__(self, strips: list[Strip], region: Region) -> None:
def __init__(self, strips: list[Iterable[Strip]], region: Region) -> None:
self.strips = strips
self.region = region

Expand All @@ -87,7 +87,8 @@ def __rich_console__(
move_to = Control.move_to
for last, (y, line) in loop_last(enumerate(self.strips, self.region.y)):
yield move_to(x, y).segment
yield from line
for strip in line:
yield from strip
if not last:
yield new_line

Expand All @@ -102,11 +103,12 @@ def render_segments(self, console: Console) -> str:
"""
sequences: list[str] = []
append = sequences.append
extend = sequences.extend
x = self.region.x
move_to = Control.move_to
for last, (y, line) in loop_last(enumerate(self.strips, self.region.y)):
append(move_to(x, y).segment.text)
append(line.render(console))
extend([strip.render(console) for strip in line])
if not last:
append("\n")
return "".join(sequences)
Expand Down Expand Up @@ -239,7 +241,6 @@ def render_segments(self, console: Console) -> str:
Returns:
Raw data with escape sequences.
"""

sequences: list[str] = []
append = sequences.append

Expand Down Expand Up @@ -613,8 +614,9 @@ def add_widget(
- widget.scrollbar_size_horizontal
)
)
widget.set_reactive(Widget.scroll_y, new_scroll_y)
widget.set_reactive(Widget.scroll_target_y, new_scroll_y)
capped_scroll_y = widget.validate_scroll_y(new_scroll_y)
widget.set_reactive(Widget.scroll_y, capped_scroll_y)
widget.set_reactive(Widget.scroll_target_y, capped_scroll_y)
widget.vertical_scrollbar._reactive_position = new_scroll_y

if visible_only:
Expand Down Expand Up @@ -1132,14 +1134,15 @@ def render_full_update(self, simplify: bool = False) -> LayoutUpdate:
self._dirty_regions.clear()
crop = screen_region
chops = self._render_chops(crop, lambda y: True)
render_strips: list[Iterable[Strip]]
if simplify:
# Simplify is done when exporting to SVG
# It doesn't make things faster
render_strips = [
Strip.join(chop.values()).simplify().discard_meta() for chop in chops
[Strip.join(chop.values()).simplify().discard_meta()] for chop in chops
]
else:
render_strips = [Strip.join(chop.values()) for chop in chops]
render_strips = [chop.values() for chop in chops]

return LayoutUpdate(render_strips, screen_region)

Expand Down Expand Up @@ -1182,7 +1185,7 @@ def _render_chops(
self,
crop: Region,
is_rendered_line: Callable[[int], bool],
) -> Sequence[Mapping[int, Strip | None]]:
) -> Sequence[Mapping[int, Strip]]:
"""Render update 'chops'.

Args:
Expand Down Expand Up @@ -1221,8 +1224,7 @@ def _render_chops(
for cut, strip in zip(final_cuts, cut_strips):
if get_chops_line(cut) is None:
chops_line[cut] = strip

return chops
return cast("Sequence[Mapping[int, Strip]]", chops)

def __rich__(self) -> StripRenderable:
return StripRenderable(self.render_strips())
Expand Down
8 changes: 5 additions & 3 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4576,9 +4576,11 @@ def suspend(self) -> Iterator[None]:
# app, and we don't want to have the driver auto-restart
# application mode when the application comes back to the
# foreground, in this context.
with self._driver.no_automatic_restart(), redirect_stdout(
sys.__stdout__
), redirect_stderr(sys.__stderr__):
with (
self._driver.no_automatic_restart(),
redirect_stdout(sys.__stdout__),
redirect_stderr(sys.__stderr__),
):
yield
# We're done with the dev's code so resume application mode.
self._driver.resume_application_mode()
Expand Down
4 changes: 2 additions & 2 deletions src/textual/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,8 @@ def assemble(
def simplify(self) -> Content:
"""Simplify spans by joining contiguous spans together.

This can produce faster renders but typically only worth it if you have appended a
large number of Content instances together.
This may produce faster renders if you have concatenated a large number of small pieces
of content with repeating styles.

Note that this modifies the Content instance in-place, which might appear
to violate the immutability constraints, but it will not change the rendered output,
Expand Down
17 changes: 12 additions & 5 deletions src/textual/strip.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,21 @@ def join(cls, strips: Iterable[Strip | None]) -> Strip:
Returns:
A new combined strip.
"""
join_strips = [strip for strip in strips if strip is not None]
join_strips = [
strip for strip in strips if strip is not None and strip.cell_count
]
segments = [segment for strip in join_strips for segment in strip._segments]
cell_length: int | None = None
if any([strip._cell_length is None for strip in join_strips]):
cell_length = None
else:
cell_length = sum([strip._cell_length or 0 for strip in join_strips])
return cls(segments, cell_length)
joined_strip = cls(segments, cell_length)
if all(strip._render_cache is not None for strip in join_strips):
joined_strip._render_cache = "".join(
[strip._render_cache for strip in join_strips]
)
return joined_strip

def __add__(self, other: Strip) -> Strip:
return Strip.join([self, other])
Expand Down Expand Up @@ -579,9 +586,7 @@ def divide(self, cuts: Iterable[int]) -> Sequence[Strip]:
cell_length = self.cell_length
cuts = [cut for cut in cuts if cut <= cell_length]
cache_key = tuple(cuts)
cached = self._divide_cache.get(cache_key)

if cached is not None:
if (cached := self._divide_cache.get(cache_key)) is not None:
return cached

strips: list[Strip]
Expand Down Expand Up @@ -721,6 +726,7 @@ def render(self, console: Console) -> str:
for text, style, _ in self._segments
]
)

return self._render_cache

def crop_pad(self, cell_length: int, left: int, right: int, style: Style) -> Strip:
Expand Down Expand Up @@ -805,5 +811,6 @@ def apply_offsets(self, x: int, y: int) -> Strip:
)
x += len(segment.text)
strip = Strip(strip_segments, self._cell_length)
strip._render_cache = self._render_cache
self._offsets_cache[cache_key] = strip
return strip
7 changes: 6 additions & 1 deletion src/textual/widgets/_text_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -1485,7 +1485,10 @@ def _render_line(self, y: int) -> Strip:
line_style = theme.base_style if theme else None

text_strip = text_strip.extend_cell_length(target_width, line_style)
strip = Strip.join([Strip(gutter, cell_length=gutter_width), text_strip])
if gutter:
strip = Strip.join([Strip(gutter, cell_length=gutter_width), text_strip])
else:
strip = text_strip

return strip.apply_style(base_style)

Expand Down Expand Up @@ -2343,6 +2346,8 @@ def insert(
Returns:
An `EditResult` containing information about the edit.
"""
if len(text) > 1:
self._restart_blink()
if location is None:
location = self.cursor_location
return self.edit(Edit(text, location, location, maintain_selection_offset))
Expand Down
Loading