mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 16:58:06 +00:00
feat(layout): fit_text_proportional gains an axis-specific scale override
self.scale (min(width_ratio, height_ratio)) is the right conservative default for anything whose aspect ratio matters, but a caller whose surrounding composition already scales along a single axis — e.g. football-scoreboard's logo_slot = min(height, width // 2), which tracks height alone — needs text sized the same way, or it reads as under-scaled next to logos that grew on a panel that only got taller (128x32 -> 128x64: self.scale stays 1.0 since width didn't grow, but logos still double). fit_text_proportional(..., scale=None) now accepts an explicit override; None keeps the existing self.scale default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -87,7 +87,7 @@ Built per (width, height); exposes facts and fit queries:
|
||||
at-or-below the panel's tier.
|
||||
- `fit_text(text, box, ladder, ellipsis=True)` → `FitResult` — largest rung
|
||||
that fits; ellipsizes as a last resort. Cached per (text, box, ladder).
|
||||
- `fit_text_proportional(text, box, base_size_px, ladder, ellipsis=True)` —
|
||||
- `fit_text_proportional(text, box, base_size_px, ladder, ellipsis=True, scale=None)` —
|
||||
rung closest to (not exceeding) `base_size_px * scale`, still capped to
|
||||
what fits the box. Use this instead of `fit_text` when several
|
||||
independently-fitted elements need to stay visually harmonious as the
|
||||
@@ -96,7 +96,12 @@ Built per (width, height); exposes facts and fit queries:
|
||||
out of proportion to a neighbor that scales by geometry (e.g. logos
|
||||
sized via `px()`), even though each individual pick is "correct" in
|
||||
isolation. `base_size_px` is normally the element's existing classic/
|
||||
fixed font size, so it scales in step with everything else.
|
||||
fixed font size. `scale` defaults to `self.scale` (the conservative
|
||||
min-of-both-axes factor `px()` uses); pass an axis-specific value when
|
||||
the surrounding composition already scales that way — e.g. a scoreboard
|
||||
whose logo slots track height alone (`min(height, width // 2)`) should
|
||||
size its text by `height / design_height` too, or the text reads as
|
||||
under-scaled next to bigger logos on a panel that only grew taller.
|
||||
- `fit_lines(lines, box, ladder, spacing)` — every line fits the width and
|
||||
the stack fits the height (measures the actual strings).
|
||||
- `font_for_rows(rows, box_h, ladder)` — largest rung whose line height
|
||||
|
||||
+16
-6
@@ -382,8 +382,9 @@ class LayoutContext:
|
||||
|
||||
def fit_text_proportional(self, text: str, box: Union[Region, Tuple[int, int]],
|
||||
base_size_px: int, ladder: FontLadder = LADDER_DEFAULT,
|
||||
ellipsis: bool = True) -> FitResult:
|
||||
"""Ladder rung closest to (but not exceeding) ``base_size_px * self.scale``
|
||||
ellipsis: bool = True,
|
||||
scale: Optional[float] = None) -> FitResult:
|
||||
"""Ladder rung closest to (but not exceeding) ``base_size_px * scale``
|
||||
that still fits the box — proportional sizing instead of ``fit_text``'s
|
||||
"always maximize" behavior.
|
||||
|
||||
@@ -396,8 +397,16 @@ class LayoutContext:
|
||||
independently "correct". ``base_size_px`` is the size that element
|
||||
renders at on the design size (``design_size``, typically 128x32)
|
||||
— commonly a plugin's existing classic/fixed font size for that
|
||||
element, so proportional sizing tracks the same geometry scale as
|
||||
everything else in ``px()``.
|
||||
element.
|
||||
|
||||
``scale`` defaults to ``self.scale`` (the same conservative
|
||||
min(width_ratio, height_ratio) factor ``px()`` uses — safe for
|
||||
content whose aspect ratio matters). Pass an explicit axis-specific
|
||||
value when the surrounding composition already scales that way —
|
||||
e.g. a scoreboard whose logos scale with height alone
|
||||
(``logo_slot = min(height, width // 2)``) should size its score
|
||||
text by ``height / design_height`` too, or its text will look
|
||||
under-scaled next to bigger logos on a panel that only grew taller.
|
||||
|
||||
Falls back to the smallest rung when even that exceeds the target
|
||||
(a tiny scale factor), and to fit_text's ordinary smaller-rung
|
||||
@@ -405,11 +414,12 @@ class LayoutContext:
|
||||
box.
|
||||
"""
|
||||
box_w, box_h = _box_dims(box)
|
||||
key = ("text_prop", text, box_w, box_h, ladder, base_size_px, ellipsis)
|
||||
effective_scale = self.scale if scale is None else scale
|
||||
key = ("text_prop", text, box_w, box_h, ladder, base_size_px, ellipsis, effective_scale)
|
||||
cached = self._fit_cache.get(key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
target = base_size_px * self.scale
|
||||
target = base_size_px * effective_scale
|
||||
eligible = [step for step in ladder if step.size_px <= target]
|
||||
candidates = eligible if eligible else (min(ladder, key=lambda s: s.size_px),)
|
||||
result = self._walk_ladder(text, candidates, box_w, box_h, ellipsis)
|
||||
|
||||
@@ -274,6 +274,20 @@ class TestFontFitting:
|
||||
second = ctx.fit_text_proportional("X", ctx.bounds, base_size_px=10)
|
||||
assert first is second
|
||||
|
||||
def test_fit_text_proportional_scale_override(self, font_manager):
|
||||
# 128x64 vs design 128x32: self.scale (min of both axes) is 1.0
|
||||
# since width didn't grow, but a caller whose composition scales by
|
||||
# HEIGHT alone (e.g. logo_slot = min(h, w//2)) should be able to
|
||||
# override the reference scale so text grows with it too.
|
||||
ctx = LayoutContext(128, 64, font_manager)
|
||||
assert ctx.scale == 1.0
|
||||
default_fit = ctx.fit_text_proportional("17-21", ctx.bounds, base_size_px=10,
|
||||
ladder=LADDER_ARCADE)
|
||||
height_scale = 64 / 32 # matches design height
|
||||
scaled_fit = ctx.fit_text_proportional("17-21", ctx.bounds, base_size_px=10,
|
||||
ladder=LADDER_ARCADE, scale=height_scale)
|
||||
assert scaled_fit.size_px > default_fit.size_px
|
||||
|
||||
def test_fit_lines_stacks_within_height(self, ctx):
|
||||
box = ctx.bounds
|
||||
lines = ["LINE ONE", "LINE TWO", "LINE THREE"]
|
||||
|
||||
Reference in New Issue
Block a user