From 486968105bf130e1d16f5911f809fc0d927181c8 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Thu, 8 Oct 2026 03:17:29 -0400 Subject: [PATCH] Paint captured keylines from the acquired geometry --- src/textual/layout.py | 5 +++- tests/test_viewport_geometry_targets.py | 40 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/textual/layout.py b/src/textual/layout.py index 8fb208c2c9..8e37780915 100644 --- a/src/textual/layout.py +++ b/src/textual/layout.py @@ -336,7 +336,10 @@ def render_keyline(self, container: Widget) -> StripRenderable: Returns: A renderable to draw the keylines. """ - width, height = container.outer_size + # Keylines and their child rectangles belong to the same geometry. + # A complete subtree capture admits offscreen boxes before those + # widgets receive resize notifications; outer_size is that older value. + width, height = container.region.size canvas = Canvas(width, height) line_style, keyline_color = container.styles.keyline diff --git a/tests/test_viewport_geometry_targets.py b/tests/test_viewport_geometry_targets.py index b6d0770592..8dceadfa67 100644 --- a/tests/test_viewport_geometry_targets.py +++ b/tests/test_viewport_geometry_targets.py @@ -8,6 +8,46 @@ from textual.widgets import Static +async def test_complete_markdown_capture_keeps_offscreen_table_keylines(): + from textual.widgets import Markdown + from textual.widgets._markdown import MarkdownTableContent + + source = "\n\n".join( + f"| key | value |\n| --- | --- |\n| TABLE_{index:02} | owned source |" + for index in range(12) + ) + body = Markdown(source) + + class TableScreen(Screen): + def _use_viewport_layout(self): + return True + + def compose(self): + with VerticalScroll(): + yield body + + app = App() + async with app.run_test(size=(60, 12)) as pilot: + screen = TableScreen() + await app.push_screen(screen) + await pilot.pause() + compositor = screen._compositor + tables = list(body.query(MarkdownTableContent)) + assert len(tables) == 12 + assert tables[-1].outer_size.height == 0 + assert tables[-1] not in compositor._published_map + _, placement = next(compositor.published_geometry((body,))) + published = compositor._full_map, compositor._visible_map + _, strips = compositor.render_subtree_strips(body, placement) + rows = [strip.text for strip in strips] + for index in range(12): + row = next(row for row in rows if f"TABLE_{index:02}" in row) + assert "│" in row, f"Offscreen table {index} lost its captured keyline" + assert compositor._full_map is published[0] + assert compositor._visible_map is published[1] + assert tables[-1].outer_size.height == 0 + + class TargetedScreen(Screen): CSS = "VerticalScroll { width: 1fr; } VerticalGroup, Static { height: auto; }" targets = ()