On a short frame, a graph's caption and node-type key step aside
Message paths and the mesh walk keep their list's rows on the Cardputer instead of spending three on a key to marks that mean the same thing everywhere; the rule is widgets.short_frame, the same one that draws a tab strip on one row. The message paths budget also counts the key's real height, which wraps to two lines at 53 columns and had been counted as one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5675ba0e1f
commit
390a0a1616
6 files changed
+42
-30
No files matched your search
@@ -75,6 +75,7 @@ from .widgets import (
|
||||
revisit_note,
|
||||
route_graph_style,
|
||||
scope_text,
|
||||
short_frame,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -322,20 +323,25 @@ class MessagePathsScreen(Screen):
|
||||
# No blank line above the graph: its canvas already opens with air over the
|
||||
# topmost lane, so a spacer here would read as two rows of margin.
|
||||
revisit = self._revisit_line(width)
|
||||
# The fan's own trailing chrome (caption, node-type key, the revisit note) plus the
|
||||
# blank that sets the list apart — all struck from the budget before the ceiling is.
|
||||
chrome = 2 + len(revisit) + 1
|
||||
room = viewport - len(lines) - chrome - min(sum(heights), _LIST_MIN_LINES)
|
||||
graph = self._graph_lines(width, max(_GRAPH_MIN_ROWS, min(_GRAPH_MAX_ROWS, room)))
|
||||
if graph:
|
||||
lines.extend(graph)
|
||||
# The fan's caption and node-type key, which a short frame leaves out: the marks
|
||||
# mean one thing app-wide, and the rows go to the list (see widgets.short_frame).
|
||||
key: list[str] = []
|
||||
if not short_frame(viewport):
|
||||
far = "you" if not self._destination else self._destination
|
||||
caption = Text(
|
||||
f"origin → {far} · white = selected path · labels = hash byte",
|
||||
style="faint",
|
||||
)
|
||||
lines.append(render_to_ansi(caption, width, no_wrap=True))
|
||||
lines.extend(render_lines(node_type_legend(width=width), width, no_wrap=True))
|
||||
key.append(render_to_ansi(caption, width, no_wrap=True))
|
||||
key.extend(render_lines(node_type_legend(width=width), width, no_wrap=True))
|
||||
# The fan's own trailing chrome (that key, the revisit note) plus the blank that
|
||||
# sets the list apart — all struck from the budget before the ceiling is.
|
||||
chrome = len(key) + len(revisit) + 1
|
||||
room = viewport - len(lines) - chrome - min(sum(heights), _LIST_MIN_LINES)
|
||||
graph = self._graph_lines(width, max(_GRAPH_MIN_ROWS, min(_GRAPH_MAX_ROWS, room)))
|
||||
if graph:
|
||||
lines.extend(graph)
|
||||
lines.extend(key)
|
||||
lines.extend(revisit)
|
||||
lines.append("")
|
||||
|
||||
|
||||
@@ -131,8 +131,8 @@ from .widgets import (
|
||||
format_ago,
|
||||
highlighted_hash,
|
||||
node_type_legend,
|
||||
short_frame,
|
||||
tab_air,
|
||||
tab_compact,
|
||||
tab_strip,
|
||||
)
|
||||
|
||||
@@ -767,7 +767,7 @@ class NodeDetailScreen(Screen):
|
||||
[t.name for t in self._tabs],
|
||||
self._tab_index,
|
||||
width,
|
||||
compact=tab_compact(viewport),
|
||||
compact=short_frame(viewport),
|
||||
),
|
||||
width,
|
||||
no_wrap=True,
|
||||
|
||||
@@ -62,8 +62,8 @@ from .widgets import (
|
||||
node_type_legend,
|
||||
route_graph_style,
|
||||
self_marker,
|
||||
short_frame,
|
||||
tab_air,
|
||||
tab_compact,
|
||||
tab_strip,
|
||||
)
|
||||
|
||||
@@ -649,7 +649,7 @@ class RecordScreen(Screen):
|
||||
lines: list[str] = [render_to_ansi(self._header(), width, no_wrap=True)]
|
||||
lines.extend([""] * tab_air())
|
||||
strip = tab_strip(
|
||||
self._tabs, self._tab_index, width, compact=tab_compact(self._scroll_viewport)
|
||||
self._tabs, self._tab_index, width, compact=short_frame(self._scroll_viewport)
|
||||
)
|
||||
lines.extend(render_lines(strip, width))
|
||||
lines.extend([""] * tab_air())
|
||||
|
||||
@@ -76,7 +76,7 @@ from .theme import mark_rgb, name_style, snr_style
|
||||
from .trace_screen import snr_bar
|
||||
from .tui.render import crop_cells, query_line, render_to_ansi
|
||||
from .tui.screen import ListWindow, Screen
|
||||
from .widgets import DEFAULT_GLYPH, NODE_GLYPHS, format_age, highlighted_hash
|
||||
from .widgets import DEFAULT_GLYPH, NODE_GLYPHS, format_age, highlighted_hash, short_frame
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..context import AppContext
|
||||
@@ -557,14 +557,18 @@ class WalkScreen(Screen):
|
||||
viewport = self._scroll_viewport # recorded by the frame before this render
|
||||
|
||||
header = self._header_lines(width, depths)
|
||||
chrome = len(header) + 3 + self._query_row() # legend, blank, list heading (+find echo)
|
||||
# The legend and the blank under it go on a short frame, where the rows are the
|
||||
# list's (see widgets.short_frame); the list's heading stays either way.
|
||||
keyed = not short_frame(viewport)
|
||||
chrome = len(header) + (3 if keyed else 1) + self._query_row() # (+find echo)
|
||||
canvas_h = self._canvas_height(viewport, chrome)
|
||||
list_win = max(1, viewport - chrome - canvas_h)
|
||||
|
||||
lines: list[str] = list(header)
|
||||
lines.extend(self._canvas_lines(width, canvas_h, selected))
|
||||
lines.append(render_to_ansi(self._legend(), width, no_wrap=True))
|
||||
lines.append("")
|
||||
if keyed:
|
||||
lines.append(render_to_ansi(self._legend(), width, no_wrap=True))
|
||||
lines.append("")
|
||||
lines.extend(self._list_lines(width, rows, depths, list_win))
|
||||
self._scroll_total = max(1, len(lines))
|
||||
return lines
|
||||
|
||||
+12
-10
@@ -1247,17 +1247,19 @@ def tab_air() -> int:
|
||||
return _TAB_AIR
|
||||
|
||||
|
||||
#: The viewport, in rows, below which a tabbed page draws its strip on one row (see
|
||||
#: :func:`tab_strip`'s ``compact``). The boxed strip costs three rows and its air; on the
|
||||
#: Cardputer's 11-row viewport that was the difference between a page's actions on screen
|
||||
#: and below the fold. Read from the viewport rather than the platform, so a desktop
|
||||
#: terminal dragged short gets the same answer the handheld does.
|
||||
COMPACT_TABS_BELOW = 16
|
||||
#: The viewport, in rows, below which a frame is *short* and spends its rows on content:
|
||||
#: a tabbed page draws its strip on one row (see :func:`tab_strip`'s ``compact``), and a
|
||||
#: graph's caption and node-type key step aside so the list under it gets the rows. The
|
||||
#: boxed strip alone costs three; on the Cardputer's 11-row viewport that was the
|
||||
#: difference between a page's actions on screen and below the fold. Read from the
|
||||
#: viewport rather than the platform, so a desktop terminal dragged short gets the same
|
||||
#: answer the handheld does.
|
||||
SHORT_FRAME_BELOW = 16
|
||||
|
||||
|
||||
def tab_compact(viewport: int) -> bool:
|
||||
"""Whether a page with ``viewport`` rows draws its tab strip compact (one row)."""
|
||||
return viewport < COMPACT_TABS_BELOW
|
||||
def short_frame(viewport: int) -> bool:
|
||||
"""Whether a page with ``viewport`` rows is short (see :data:`SHORT_FRAME_BELOW`)."""
|
||||
return viewport < SHORT_FRAME_BELOW
|
||||
|
||||
|
||||
def tab_strip(labels: Sequence[str], active: int, width: int, *, compact: bool = False) -> Group:
|
||||
@@ -1290,7 +1292,7 @@ def tab_strip(labels: Sequence[str], active: int, width: int, *, compact: bool =
|
||||
owning screen switches the active index (``Tab``/``Shift+Tab``); the strip itself is
|
||||
pure presentation.
|
||||
|
||||
Where rows are scarcer than that (``compact``, see :func:`tab_compact`) the same strip is
|
||||
Where rows are scarcer than that (``compact``, see :func:`short_frame`) the same strip is
|
||||
drawn on its own bottom rule, in one row: ``──┤ Info ├── Routes ──────``. The active tab
|
||||
stands on the rule between tees, lit; an inactive one is its label on the rule, faint.
|
||||
Every tab is the same width in either state, so switching still never moves a label.
|
||||
|
||||
@@ -290,10 +290,10 @@ def test_every_advert_type_has_one_name() -> None:
|
||||
|
||||
|
||||
def test_a_compact_tab_strip_is_one_row_and_never_moves_a_label() -> None:
|
||||
"""Below COMPACT_TABS_BELOW rows the strip draws on its rule, every tab a fixed width."""
|
||||
from meshterm.ui.widgets import COMPACT_TABS_BELOW, tab_compact, tab_strip
|
||||
"""Below SHORT_FRAME_BELOW rows the strip draws on its rule, every tab a fixed width."""
|
||||
from meshterm.ui.widgets import SHORT_FRAME_BELOW, short_frame, tab_strip
|
||||
|
||||
assert tab_compact(COMPACT_TABS_BELOW - 1) and not tab_compact(COMPACT_TABS_BELOW)
|
||||
assert short_frame(SHORT_FRAME_BELOW - 1) and not short_frame(SHORT_FRAME_BELOW)
|
||||
rows = [tab_strip(["Info", "Routes", "Area"], i, 40, compact=True) for i in range(3)]
|
||||
plains = [group.renderables[0].plain for group in rows]
|
||||
assert all(len(group.renderables) == 1 for group in rows)
|
||||
|
||||
Reference in new issue
Block a user