From e27e79962c23ef08eb3533123d69e260d817dcbc Mon Sep 17 00:00:00 2001 From: gramps Date: Fri, 30 Oct 2026 15:09:19 -0700 Subject: [PATCH] fix(scoreboard): box follows the rendered strip, opacity proven, plate always settable, dev override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- Controls/LeftPanel.xaml | 5 ++--- ViewModels/MainViewModel.Scoreboard.cs | 18 +++++++++++++++++- ytLive.Tests/ScoreboardRendererTests.cs | 21 +++++++++++++++++++++ ytLive.Tests/ScoreboardSimulationTests.cs | 5 +++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/Controls/LeftPanel.xaml b/Controls/LeftPanel.xaml index 3de42fd..1ef1ba3 100644 --- a/Controls/LeftPanel.xaml +++ b/Controls/LeftPanel.xaml @@ -502,9 +502,8 @@ IsChecked="{Binding ScoreboardBackgroundEnabled, Mode=TwoWay}"/> - + SelectedValuePath="Hex" SelectedValue="{Binding ScoreboardBackgroundColor, Mode=TwoWay}"/> + Gap between the strip and the frame edge. + private const double ScoreboardEdgeMargin = 8; private Source? _scoreboardPreviewSource; internal ScoreboardRecorder ScoreboardRecorder => _scoreboard; @@ -200,7 +203,9 @@ public partial class MainViewModel /// must never touch a WriteableBitmap, so this runs on the UI thread only. private void RefreshScoreboardPreviews() { - var frame = _scoreboardEnabled ? _scoreboardPresenter?.Frame : null; + // Dev simulation OVERRIDES the master switch (creator ruling): the whole point of the dev + // toggle is to prove the strip without touching product settings. + var frame = (_scoreboardEnabled || _scoreboardSimulate) ? _scoreboardPresenter?.Frame : null; foreach (var scene in Scenes) foreach (var source in scene.Elements.OfType().Where(s => s.IsScoreboard)) { @@ -213,6 +218,17 @@ public partial class MainViewModel _scoreboardPreview.WritePixels(new System.Windows.Int32Rect(0, 0, frame.Width, frame.Height), frame.BgraPixels, frame.Width * 4, 0); source.VideoImageSource = _scoreboardPreview; + + // The strip's height follows what was RENDERED (one line or two). A fixed 56px box + // squashed a wrapped strip instead of showing it, and its Y follows from the edge. + if (Math.Abs(source.Height - frame.Height) > 0.5) + { + source.Width = frame.Width; + source.Height = frame.Height; + source.Y = _scoreboardStyle is var _ && source.Y > 540 + ? 1080 - frame.Height - ScoreboardEdgeMargin + : ScoreboardEdgeMargin; + } _scoreboardPreviewSource = source; } } diff --git a/ytLive.Tests/ScoreboardRendererTests.cs b/ytLive.Tests/ScoreboardRendererTests.cs index 5c63460..9f397bb 100644 --- a/ytLive.Tests/ScoreboardRendererTests.cs +++ b/ytLive.Tests/ScoreboardRendererTests.cs @@ -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(), new ScoreboardStyle())); } diff --git a/ytLive.Tests/ScoreboardSimulationTests.cs b/ytLive.Tests/ScoreboardSimulationTests.cs index 9a521a6..c76cae7 100644 --- a/ytLive.Tests/ScoreboardSimulationTests.cs +++ b/ytLive.Tests/ScoreboardSimulationTests.cs @@ -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);