fix(scoreboard): box follows the rendered strip, opacity proven, plate always settable, dev override

Four reports, three causes:

1. "Background plate has no code behind it" — the plate COLOUR and OPACITY controls were disabled
   until the plate switch was on, which reads as dead controls. They are always settable now; the
   switch is what decides whether a plate is drawn.
2. "Opacity doesn't work" — proven in the renderer test: 20% and 80% produce alphas of ~51 and ~204,
   so the slider reaches the pixels. What was actually wrong was the box.
3. The strip's BOX was fixed at 56px while a wrapped strip renders taller — the frame was scaled
   into the box and looked squashed/clipped. The source's width/height now follow the rendered
   frame, and its Y follows from the edge.
4. "Simulate (dev) should override Show the scoreboard" — it does: the preview gate is
   (_scoreboardEnabled || _scoreboardSimulate), so the dev toggle works even with the product
   setting off.

Not changed: the strip is STATIC by design (all enabled facts always visible, wrap rather than
scroll). If scrolling is wanted after all, that is a presentation mode to add, not a fix.
This commit is contained in:
2026-10-30 15:09:19 -07:00
parent 8caee21b9a
commit e27e79962c
4 changed files with 45 additions and 4 deletions
+21
View File
@@ -44,6 +44,27 @@ public class ScoreboardRendererTests
var red = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FF0000", false, "#000000", 0.5));
Assert.True(Count(red!, (r, g, b, a) => r > 180 && g < 80 && b < 80 && a > 200) > 20);
// The PLATE TOGGLE itself: same text, plate off vs on, different pixels. This is the
// check that answers "the checkbox has no code behind it".
var off = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", false, "#0000FF", 0.5));
var on = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", true, "#0000FF", 0.5));
Assert.True(Count(on!, (r, g, b, a) => b > 150 && r < 60 && g < 60 && a > 100)
> Count(off!, (r, g, b, a) => b > 150 && r < 60 && g < 60 && a > 100) + 500,
"turning the plate on must add plate pixels");
// Opacity: 20% and 80% must produce visibly different plate transparency.
var faint = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", true, "#0000FF", 0.2));
var solid = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", true, "#0000FF", 0.8));
int AlphaOf(VideoFrame f)
{
for (var i = 3; i < f.BgraPixels.Length; i += 4)
if (f.BgraPixels[i] is > 10 and < 245) return f.BgraPixels[i];
return 0;
}
Assert.InRange(AlphaOf(faint!), 35, 70); // ~20% of 255
Assert.InRange(AlphaOf(solid!), 190, 220); // ~80% of 255
Assert.True(AlphaOf(solid!) > AlphaOf(faint!) + 100, "the opacity slider must move the alpha");
// Nothing enabled: no frame at all (the layer draws nothing).
Assert.Null(ScoreboardRenderer.Render(Array.Empty<string>(), new ScoreboardStyle()));
}
@@ -29,6 +29,11 @@ public class ScoreboardSimulationTests
recorder.StartSession();
Assert.Equal(0, recorder.State.NewMembers);
// Simulate OVERRIDES the master "show" switch: the dev toggle must work even when the
// product setting is off.
var vmSource = SourceAssert.Text("ViewModels", "MainViewModel.Scoreboard.cs");
Assert.Contains("_scoreboardEnabled || _scoreboardSimulate", vmSource);
// The control exists and is gated on the build, not on a runtime flag.
var panel = SourceAssert.Text("Controls", "LeftPanel.xaml");
Assert.Contains("ScoreboardSimulate", panel);