A share code is centred as a block, never justified — the bottom finder was skewed

Rich's centre justify strips each row's trailing spaces before it pads it, so a
code row whose right edge was light modules lost cells and landed a column off
its neighbours; the bottom-left finder square came out one cell skewed, and a
camera could not lock on. The About pages' codes were never centred, which is
why only the full-screen share codes broke.

A code is now moved across a line one way only: the same indent in front of
every row, computed once from the fitted code's width. The test pins that all
rows share a width and every finder starts in the same column.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Fable 5.1 committed 2026-09-12 03:29:46 -04:00
1 parent 4b605c1da7
commit 197af90c8e
2 files changed
+36 -14

No files matched your search

+30 -14
View File
@@ -59,8 +59,15 @@ _STYLE = "qr"
_FITS: tuple[tuple[str, int], ...] = (("m", 4), ("l", 4), ("m", 2), ("l", 2))
def _draw(code: segno.QRCode, border: int) -> Text:
"""Draw a segno code as half-block rows, ``border`` light modules around it."""
def _draw(code: segno.QRCode, border: int, *, indent: int = 0) -> Text:
"""Draw a segno code as half-block rows, ``border`` light modules around it.
``indent`` is the one way a code is ever moved across a line: the same run of bare
cells in front of *every* row. A code must never be justified — Rich's centring
strips each row's trailing spaces before it pads, so a row whose right edge is light
modules loses cells and lands a column off its neighbours, and a finder square one
row skewed is a code no camera can lock on to.
"""
rows = [[bool(v) for v in row] for row in code.matrix_iter(border=border)]
# Pair rows top-to-bottom; pad an odd final row with light modules so the last
# half-block cell renders cleanly.
@@ -71,6 +78,8 @@ def _draw(code: segno.QRCode, border: int) -> Text:
for i, (top, bottom) in enumerate(zip(rows[0::2], rows[1::2], strict=True)):
if i:
text.append("\n")
if indent:
text.append(" " * indent)
line = "".join(_GLYPH[(t, b)] for t, b in zip(top, bottom, strict=True))
text.append(line, style=_STYLE)
return text
@@ -93,8 +102,8 @@ def qr_text(data: str, *, error: str = "m", border: int = _BORDER) -> Text:
return _draw(segno.make(data, error=error), border)
def _fit(data: str, width: int, height: int | None) -> Text | None:
"""The code at the first of :data:`_FITS` within ``width`` cells and ``height`` rows.
def _fit(data: str, width: int, height: int | None) -> tuple[segno.QRCode, int] | None:
"""The first of :data:`_FITS` within ``width`` cells and ``height`` rows, as (code, border).
``None`` when no fit is that small — the caller decides what to relax.
"""
@@ -104,10 +113,18 @@ def _fit(data: str, width: int, height: int | None) -> Text | None:
code = segno.make(data, error=error)
modules = code.symbol_size(border=border)[0]
if modules <= width and (height is None or (modules + 1) // 2 <= height):
return _draw(code, border)
return code, border
return None
def _smallest(data: str) -> tuple[segno.QRCode, int]:
"""The last of :data:`_FITS` — what a frame too small for any fit gets anyway."""
import segno
error, border = _FITS[-1]
return segno.make(data, error=error), border
def fit_qr(data: str, width: int, height: int | None = None) -> Text:
"""The code for ``data`` at the first of :data:`_FITS` that fits the space given.
@@ -122,11 +139,8 @@ def fit_qr(data: str, width: int, height: int | None = None) -> Text:
Returns:
The code, as :func:`qr_text` draws it.
"""
fitted = _fit(data, width, height)
if fitted is None:
error, border = _FITS[-1]
fitted = qr_text(data, error=error, border=border)
return fitted
code, border = _fit(data, width, height) or _smallest(data)
return _draw(code, border)
class QrScreen(ScrollScreen):
@@ -166,13 +180,15 @@ class QrScreen(ScrollScreen):
rows = self._scroll_viewport
link = Text(self.url, style="accent", justify="center")
link_rows = len(render_lines(link, width))
code = (
code, border = (
_fit(self.url, width, rows - 1 - link_rows)
or _fit(self.url, width, rows)
or fit_qr(self.url, width)
or _fit(self.url, width, None)
or _smallest(self.url)
)
code.justify = "center"
self.replace_content(Group(code, Text(""), link))
# Centred as a block — one indent for every row (see _draw), never justified.
indent = max(0, (width - code.symbol_size(border=border)[0]) // 2)
self.replace_content(Group(_draw(code, border, indent=indent), Text(""), link))
return super().render_body(width)
+6
View File
@@ -269,6 +269,12 @@ def test_the_share_screen_refits_its_code_to_the_frame_every_paint() -> None:
roomy = code_rows(120, 40)
assert len(roomy) == 29 # the standard fit, where there is room for it
assert roomy[0].startswith(" " * 30), "centred across the whole width"
# Centred as a block, never per row: every row carries the same indent and the same
# width, so the finder squares stack square. Per-row centring stripped each row's
# trailing light modules before padding it, and skewed the bottom finder a cell.
assert len({len(row) for row in roomy}) == 1, "rows differ in width — a skewed code"
finder = "█▀▀▀▀▀█"
assert len({row.index(finder) for row in roomy if finder in row}) == 1, "a finder is skewed"
assert len(code_rows(72, 24)) <= 24 # a regular terminal: the code whole, link below
assert len(code_rows(53, 26)) <= 26 # the PicoCalc: likewise