optimized line_crop

This commit is contained in:
Will McGugan
2022-04-07 10:46:33 +01:00
parent 09580fe6d4
commit b6ce952716
4 changed files with 111 additions and 26 deletions

View File

@@ -28,6 +28,7 @@ from .geometry import Region, Offset, Size
from ._loop import loop_last
from ._segment_tools import line_crop
from ._types import Lines
from .widget import Widget
@@ -412,7 +413,6 @@ class Compositor:
else:
widget_regions = []
divide = Segment.divide
intersection = Region.intersection
overlaps = Region.overlaps
@@ -425,9 +425,9 @@ class Compositor:
new_x, new_y, new_width, new_height = intersection(region, clip)
delta_x = new_x - region.x
delta_y = new_y - region.y
splits = [delta_x, delta_x + new_width]
crop_x = delta_x + new_width
lines = widget.get_render_lines(delta_y, delta_y + new_height)
lines = [list(divide(line, splits))[1] for line in lines]
lines = [line_crop(line, delta_x, crop_x) for line in lines]
yield region, clip, lines
@classmethod
@@ -509,13 +509,11 @@ class Compositor:
crop_x, crop_y, crop_x2, crop_y2 = crop_region.corners
render_lines = self._assemble_chops(chops[crop_y:crop_y2])
def width_view(line: list[Segment]) -> list[Segment]:
div_lines = list(divide(line, [crop_x, crop_x2]))
line = div_lines[1] if len(div_lines) > 1 else div_lines[0]
return line
if crop is not None and (crop_x, crop_x2) != (0, width):
render_lines = [width_view(line) if line else line for line in render_lines]
render_lines = [
line_crop(line, crop_x, crop_x2) if line else line
for line in render_lines
]
return SegmentLines(render_lines, new_lines=True)

View File

@@ -1,17 +0,0 @@
from __future__ import annotations
from rich.segment import Segment
from .geometry import Region
from ._types import Lines
def crop_lines(lines: Lines, clip: Region) -> Lines:
lines = lines[clip.y : clip.y + clip.height]
def width_view(line: list[Segment]) -> list[Segment]:
_, line = Segment.divide(line, [clip.x, clip.x + clip.width])
return line
cropped_lines = [width_view(line) for line in lines]
return cropped_lines

View File

@@ -0,0 +1,49 @@
"""
Tools for processing Segments, or lists of Segments.
"""
from __future__ import annotations
from rich.segment import Segment
def line_crop(segments: list[Segment], start: int, end: int) -> list[Segment]:
"""Crops a list of segments between two cell offsets.
Args:
segments (list[Segment]): A list of Segments for a line.
start (int): Start offset
end (int): End offset
Returns:
list[Segment]: A new shorter list of segments
"""
# This is essentially a specialized version of Segment.divide
# The following line has equivalent functionality (but a little slower)
# return list(Segment.divide(segments, [start, end]))[1]
pos = 0
output_segments: list[Segment] = []
add_segment = output_segments.append
iter_segments = iter(segments)
segment: Segment | None = None
for segment in iter_segments:
end_pos = pos + segment.cell_length
if end_pos > start:
segment = segment.split_cells(start - pos)[-1]
break
pos = end_pos
else:
return []
pos = start
while segment is not None:
end_pos = pos + segment.cell_length
if end_pos < end:
add_segment(segment)
else:
add_segment(segment.split_cells(end - pos)[0])
break
pos = end_pos
segment = next(iter_segments, None)
return output_segments

View File

@@ -0,0 +1,55 @@
from rich.segment import Segment
from rich.style import Style
from textual._segment_tools import line_crop
def test_line_crop():
bold = Style(bold=True)
italic = Style(italic=True)
segments = [
Segment("Hello", bold),
Segment(" World!", italic),
]
assert line_crop(segments, 1, 2) == [Segment("e", bold)]
assert line_crop(segments, 4, 20) == [
Segment("o", bold),
Segment(" World!", italic),
]
def test_line_crop_emoji():
bold = Style(bold=True)
italic = Style(italic=True)
segments = [
Segment("Hello", bold),
Segment("💩💩💩", italic),
]
assert line_crop(segments, 8, 11) == [Segment(" 💩", italic)]
assert line_crop(segments, 9, 11) == [Segment("💩", italic)]
def test_line_crop_edge():
segments = [Segment("foo"), Segment("bar"), Segment("baz")]
assert line_crop(segments, 2, 9) == [Segment("o"), Segment("bar"), Segment("baz")]
assert line_crop(segments, 3, 9) == [Segment("bar"), Segment("baz")]
assert line_crop(segments, 4, 9) == [Segment("ar"), Segment("baz")]
assert line_crop(segments, 4, 8) == [Segment("ar"), Segment("ba")]
def test_line_crop_edge_2():
segments = [
Segment("╭─"),
Segment(
"────── Placeholder ───────",
),
Segment(
"─╮",
),
]
result = line_crop(segments, 30, 60)
expected = []
print(repr(result))
assert result == expected