Skip to content

Commit 132d56e

Browse files
committed
fix is_odd bug
1 parent 3c67916 commit 132d56e

File tree

10 files changed

+212
-161
lines changed

10 files changed

+212
-161
lines changed

src/textual/_compositor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,8 @@ def render_full_update(self, simplify: bool = False) -> LayoutUpdate:
11321132
crop = screen_region
11331133
chops = self._render_chops(crop, lambda y: True)
11341134
if simplify:
1135+
# Simplify is done when exporting to SVG
1136+
# It doesn't make things faster
11351137
render_strips = [
11361138
Strip.join(chop.values()).simplify().discard_meta() for chop in chops
11371139
]

src/textual/_node_list.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44
import weakref
55
from operator import attrgetter
6-
from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Sequence, overload
6+
from typing import TYPE_CHECKING, Any, Callable, Iterator, Sequence, overload
77

88
import rich.repr
99

@@ -41,6 +41,8 @@ def __init__(self, parent: DOMNode | None = None) -> None:
4141
# The nodes in the list
4242
self._nodes: list[Widget] = []
4343
self._nodes_set: set[Widget] = set()
44+
self._displayed_nodes: tuple[int, list[Widget]] = (-1, [])
45+
self._displayed_visible_nodes: tuple[int, list[Widget]] = (-1, [])
4446

4547
# We cache widgets by their IDs too for a quick lookup
4648
# Note that only widgets with IDs are cached like this, so
@@ -187,18 +189,29 @@ def __reversed__(self) -> Iterator[Widget]:
187189
return reversed(self._nodes)
188190

189191
@property
190-
def displayed(self) -> Iterable[Widget]:
192+
def displayed(self) -> Sequence[Widget]:
191193
"""Just the nodes where `display==True`."""
192-
for node in self._nodes:
193-
if node.display:
194-
yield node
194+
if self._displayed_nodes[0] != self._updates:
195+
self._displayed_nodes = (
196+
self._updates,
197+
[node for node in self._nodes if node.display],
198+
)
199+
return self._displayed_nodes[1]
200+
201+
@property
202+
def displayed_and_visible(self) -> Sequence[Widget]:
203+
"""Nodes with both `display==True` and `visible==True`."""
204+
if self._displayed_visible_nodes[0] != self._updates:
205+
self._displayed_nodes = (
206+
self._updates,
207+
[node for node in self.displayed if node.visible],
208+
)
209+
return self._displayed_nodes[1]
195210

196211
@property
197-
def displayed_reverse(self) -> Iterable[Widget]:
212+
def displayed_reverse(self) -> Iterator[Widget]:
198213
"""Just the nodes where `display==True`, in reverse order."""
199-
for node in reversed(self._nodes):
200-
if node.display:
201-
yield node
214+
return reversed(self.displayed)
202215

203216
if TYPE_CHECKING:
204217

src/textual/content.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,12 @@ def simplify(self) -> Content:
405405
Returns:
406406
Self.
407407
"""
408-
spans = self.spans
409-
if not spans:
408+
if not (spans := self._spans):
410409
return self
411-
last_span = Span(0, 0, Style())
410+
last_span = Span(-1, -1, "")
412411
new_spans: list[Span] = []
413412
changed: bool = False
414-
for span in self._spans:
413+
for span in spans:
415414
if span.start == last_span.end and span.style == last_span.style:
416415
last_span = new_spans[-1] = Span(last_span.start, span.end, span.style)
417416
changed = True
@@ -422,6 +421,24 @@ def simplify(self) -> Content:
422421
self._spans[:] = new_spans
423422
return self
424423

424+
def add_spans(self, spans: Sequence[Span]) -> Content:
425+
"""Adds spans to this Content instance.
426+
427+
Args:
428+
spans: A sequence of spans.
429+
430+
Returns:
431+
A Content instance.
432+
"""
433+
if spans:
434+
return Content(
435+
self.plain,
436+
[*self._spans, *spans],
437+
None if self._cell_length is None else self.cell_length,
438+
)
439+
else:
440+
return self
441+
425442
def __eq__(self, other: object) -> bool:
426443
"""Compares text only, so that markup doesn't effect sorting."""
427444
if isinstance(other, str):
@@ -741,11 +758,7 @@ def __add__(self, other: Content | str) -> Content:
741758
for start, end, style in other._spans
742759
],
743760
],
744-
(
745-
self.cell_length + other._cell_length
746-
if other._cell_length is not None
747-
else None
748-
),
761+
(self.cell_length + other.cell_length),
749762
)
750763
return content
751764
return NotImplemented
@@ -1470,7 +1483,7 @@ def highlight_regex(
14701483
self,
14711484
highlight_regex: re.Pattern[str] | str,
14721485
*,
1473-
style: Style,
1486+
style: Style | str,
14741487
maximum_highlights: int | None = None,
14751488
) -> Content:
14761489
"""Apply a style to text that matches a regular expression.

src/textual/dom.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import threading
1111
from functools import lru_cache, partial
1212
from inspect import getfile
13-
from operator import attrgetter
1413
from typing import (
1514
TYPE_CHECKING,
1615
Any,
@@ -408,6 +407,15 @@ def children(self) -> Sequence["Widget"]:
408407
"""
409408
return self._nodes
410409

410+
@property
411+
def displayed_children(self) -> Sequence[Widget]:
412+
"""The displayed children (where node.display==True).
413+
414+
Returns:
415+
A sequence of widgets.
416+
"""
417+
return self._nodes.displayed
418+
411419
@property
412420
def is_empty(self) -> bool:
413421
"""Are there no displayed children?"""
@@ -1215,15 +1223,6 @@ def ancestors(self) -> list[DOMNode]:
12151223
add_node(node)
12161224
return cast("list[DOMNode]", nodes)
12171225

1218-
@property
1219-
def displayed_children(self) -> list[Widget]:
1220-
"""The child nodes which will be displayed.
1221-
1222-
Returns:
1223-
A list of nodes.
1224-
"""
1225-
return list(filter(attrgetter("display"), self._nodes))
1226-
12271226
def watch(
12281227
self,
12291228
obj: DOMNode,

0 commit comments

Comments
 (0)