From d1f66251726b60dd2ff819b51a89417f024eb522 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Fri, 6 Nov 2026 03:54:08 -0500 Subject: [PATCH] Every visual answers whether it draws like another; only content can match content From claude-b-ui's design. Static.content is any VisualType, so draws_like belongs on Visual: by default a visual cannot tell and answers no, so it redraws. Content asks the other visual by double dispatch, passing its own text and spans, and only Content answers yes. The spans cross as Sequence[object], since the answer is equality, so visual.py names no Content type and needs no TYPE_CHECKING import. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XbFMSqJMarXn2ZE8iAL7Sd --- src/textual/content.py | 7 +++++-- src/textual/visual.py | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/textual/content.py b/src/textual/content.py index 3d3c370b78..f8c6f34d7a 100644 --- a/src/textual/content.py +++ b/src/textual/content.py @@ -523,12 +523,15 @@ def add_spans(self, spans: Sequence[Span]) -> Content: ) return self - def draws_like(self, other: Content) -> bool: + def draws_like(self, other: Visual) -> bool: """Whether both draw the same: the same text with the same styled spans. Equality compares text only, for sorting; a changed style or link is a change on screen. """ - return self.plain == other.plain and list(self._spans) == list(other._spans) + return other.draws_text(self.plain, self._spans) + + def draws_text(self, plain: str, spans: Sequence[object]) -> bool: + return self.plain == plain and list(self._spans) == list(spans) def __eq__(self, other: object) -> bool: """Compares text only, so that markup doesn't effect sorting.""" diff --git a/src/textual/visual.py b/src/textual/visual.py index 086f51cfc8..5621c12a48 100644 --- a/src/textual/visual.py +++ b/src/textual/visual.py @@ -1,6 +1,7 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Sequence from dataclasses import dataclass from itertools import islice from typing import TYPE_CHECKING, Callable, Protocol @@ -138,6 +139,14 @@ def selected_text(visual, selection): return selection.extract(str(visual)), "\n" return None + def draws_like(self, other: Visual) -> bool: + """Whether both draw the same. A visual that cannot tell answers no, so it redraws.""" + return False + + def draws_text(self, plain: str, spans: Sequence[object]) -> bool: + """Whether this draws exactly this text with these styled spans; only content can.""" + return False + @abstractmethod def render_strips( self, width: int, height: int | None, style: Style, options: RenderOptions