mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
|||||||
.pytype
|
.pytype
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
mypy_report
|
mypy_report
|
||||||
docs/build
|
docs/build
|
||||||
docs/source/_build
|
docs/source/_build
|
||||||
|
|||||||
48
src/textual/_region_group.py
Normal file
48
src/textual/_region_group.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from operator import attrgetter
|
||||||
|
from typing import NamedTuple, Iterable
|
||||||
|
|
||||||
|
from .geometry import Region
|
||||||
|
|
||||||
|
|
||||||
|
class InlineRange(NamedTuple):
|
||||||
|
"""Represents a region on a single line."""
|
||||||
|
|
||||||
|
line_index: int
|
||||||
|
start: int
|
||||||
|
end: int
|
||||||
|
|
||||||
|
|
||||||
|
def regions_to_ranges(regions: Iterable[Region]) -> Iterable[InlineRange]:
|
||||||
|
"""Converts the regions to non-overlapping horizontal strips, where each strip
|
||||||
|
represents the region on a single line. Combining the resulting strips therefore
|
||||||
|
results in a shape identical to the combined original regions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
regions (Iterable[Region]): An iterable of Regions.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Iterable[InlineRange]: Yields InlineRange objects representing the content on
|
||||||
|
a single line, with overlaps removed.
|
||||||
|
"""
|
||||||
|
inline_ranges: dict[int, list[InlineRange]] = defaultdict(list)
|
||||||
|
for region_x, region_y, width, height in regions:
|
||||||
|
for y in range(region_y, region_y + height):
|
||||||
|
inline_ranges[y].append(
|
||||||
|
InlineRange(line_index=y, start=region_x, end=region_x + width - 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
get_start = attrgetter("start")
|
||||||
|
for line_index, ranges in inline_ranges.items():
|
||||||
|
sorted_ranges = iter(sorted(ranges, key=get_start))
|
||||||
|
_, start, end = next(sorted_ranges)
|
||||||
|
for next_line_index, next_start, next_end in sorted_ranges:
|
||||||
|
if next_start <= end + 1:
|
||||||
|
end = max(end, next_end)
|
||||||
|
else:
|
||||||
|
yield InlineRange(line_index, start, end)
|
||||||
|
start = next_start
|
||||||
|
end = next_end
|
||||||
|
yield InlineRange(line_index, start, end)
|
||||||
44
tests/test_region_group.py
Normal file
44
tests/test_region_group.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from textual._region_group import regions_to_ranges, InlineRange
|
||||||
|
from textual.geometry import Region
|
||||||
|
|
||||||
|
|
||||||
|
def test_regions_to_ranges_no_regions():
|
||||||
|
assert list(regions_to_ranges([])) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_regions_to_ranges_single_region():
|
||||||
|
regions = [Region(0, 0, 3, 2)]
|
||||||
|
assert list(regions_to_ranges(regions)) == [InlineRange(0, 0, 2), InlineRange(1, 0, 2)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_regions_to_ranges_partially_overlapping_regions():
|
||||||
|
regions = [Region(0, 0, 2, 2), Region(1, 1, 2, 2)]
|
||||||
|
assert list(regions_to_ranges(regions)) == [
|
||||||
|
InlineRange(0, 0, 1), InlineRange(1, 0, 2), InlineRange(2, 1, 2),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_regions_to_ranges_fully_overlapping_regions():
|
||||||
|
regions = [Region(1, 1, 3, 3), Region(2, 2, 1, 1), Region(0, 2, 3, 1)]
|
||||||
|
assert list(regions_to_ranges(regions)) == [
|
||||||
|
InlineRange(1, 1, 3), InlineRange(2, 0, 3), InlineRange(3, 1, 3)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_regions_to_ranges_disjoint_regions_different_lines():
|
||||||
|
regions = [Region(0, 0, 2, 1), Region(2, 2, 2, 1)]
|
||||||
|
assert list(regions_to_ranges(regions)) == [InlineRange(0, 0, 1), InlineRange(2, 2, 3)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_regions_to_ranges_disjoint_regions_same_line():
|
||||||
|
regions = [Region(0, 0, 1, 2), Region(2, 0, 1, 1)]
|
||||||
|
assert list(regions_to_ranges(regions)) == [
|
||||||
|
InlineRange(0, 0, 0), InlineRange(0, 2, 2), InlineRange(1, 0, 0)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_regions_to_ranges_directly_adjacent_ranges_merged():
|
||||||
|
regions = [Region(0, 0, 1, 2), Region(1, 0, 1, 2)]
|
||||||
|
assert list(regions_to_ranges(regions)) == [
|
||||||
|
InlineRange(0, 0, 1), InlineRange(1, 0, 1)
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user