diff --git a/Controls/LeftPanel.xaml b/Controls/LeftPanel.xaml
index b0cd4b5..7b505c3 100644
--- a/Controls/LeftPanel.xaml
+++ b/Controls/LeftPanel.xaml
@@ -485,7 +485,7 @@
-
@@ -514,7 +514,7 @@
ItemsSource="{Binding ScoreboardColorOptions}" DisplayMemberPath="Name"
SelectedValuePath="Hex" SelectedValue="{Binding ScoreboardTextColor, Mode=TwoWay}"/>
-
-
-
+
diff --git a/Services/Compositor/ScoreboardPresenter.cs b/Services/Compositor/ScoreboardPresenter.cs
index ebdce80..1155608 100644
--- a/Services/Compositor/ScoreboardPresenter.cs
+++ b/Services/Compositor/ScoreboardPresenter.cs
@@ -37,7 +37,7 @@ public sealed class ScoreboardPresenter
public const double DefaultPixelsPerSecond = 120;
public const double MinPixelsPerSecond = 20;
- public const double MaxPixelsPerSecond = 400;
+ public const double MaxPixelsPerSecond = 200; // creator cap: faster than this is unreadable
/// Advances the scroll by . The strip is a marquee: one
/// measured run, cropped to a moving window, so a tick is a row-wise byte copy rather than a
diff --git a/Services/Compositor/ScoreboardRenderer.cs b/Services/Compositor/ScoreboardRenderer.cs
index 578d26f..def6f4d 100644
--- a/Services/Compositor/ScoreboardRenderer.cs
+++ b/Services/Compositor/ScoreboardRenderer.cs
@@ -15,9 +15,18 @@ public readonly record struct ScoreboardStyle(
string TextColor = "#FFFFFF",
bool BackgroundEnabled = true,
string BackgroundColor = "#000000",
- double BackgroundOpacity = 0.55,
+ double BackgroundOpacity = 0.20,
double FontSize = ScoreboardLayout.MaxFontSize,
- double SeparatorOpacity = 0.45);
+ double SeparatorOpacity = 0.45)
+{
+ /// A record STRUCT's implicit parameterless constructor zero-initialises — it does NOT
+ /// run the parameter defaults. Without this, `new ScoreboardStyle()` produced null text colour,
+ /// no background and zero opacity: every "default" in this file was a lie, and the plate/opacity
+ /// controls looked dead because the renderer was faithfully drawing what it was given.
+ public ScoreboardStyle() : this("#FFFFFF", true, "#000000", 0.20, ScoreboardLayout.MaxFontSize, 0.45)
+ {
+ }
+}
/// Rasterises the scoreboard strip (one line, shrinking/wrapping via
/// ) into a straight-alpha BGRA frame the compositor blits.
diff --git a/ViewModels/MainViewModel.Scoreboard.cs b/ViewModels/MainViewModel.Scoreboard.cs
index b6a4560..8c8cedf 100644
--- a/ViewModels/MainViewModel.Scoreboard.cs
+++ b/ViewModels/MainViewModel.Scoreboard.cs
@@ -193,6 +193,9 @@ public partial class MainViewModel
{
_scoreboardStyle = _scoreboardStyle with { BackgroundOpacity = Math.Clamp(value, 0, 1) };
PushStyle();
+ // The readout binds THIS property: without its own raise the number never moves even
+ // though the render does (creator: "bzzzt!").
+ OnPropertyChanged(nameof(ScoreboardBackgroundOpacity));
}
}
@@ -206,9 +209,14 @@ public partial class MainViewModel
FontSize = Math.Clamp(value, ScoreboardLayout.FloorFontSize, ScoreboardLayout.MaxFontSize),
};
PushStyle();
+ OnPropertyChanged(nameof(ScoreboardFontSize)); // same bug as the opacity readout
+ OnPropertyChanged(nameof(ScoreboardFontSizeLabel));
}
}
+ /// "24 px" — the panel shows real pixels, not a bare slider number.
+ public string ScoreboardFontSizeLabel => $"{_scoreboardStyle.FontSize:0} px";
+
/// The colours offered for text and plate: the app's one palette.
public IReadOnlyList ScoreboardColorOptions => BorderColorPalette.All;
diff --git a/ytLive.Tests/ScoreboardPropertiesTests.cs b/ytLive.Tests/ScoreboardPropertiesTests.cs
index ce76603..a3cda0d 100644
--- a/ytLive.Tests/ScoreboardPropertiesTests.cs
+++ b/ytLive.Tests/ScoreboardPropertiesTests.cs
@@ -25,13 +25,22 @@ public class ScoreboardPropertiesTests
Assert.Contains("Sign in to YouTube to use the scoreboard.", block);
Assert.Contains("CanUseScoreboard, Converter={StaticResource InverseBoolToVis}", block);
+ // Defaults the creator asked for, and readouts that actually move.
+ Assert.Contains("Background", block);
+ Assert.DoesNotContain("Background plate", block);
+ Assert.Contains("ScoreboardFontSizeLabel", block); // "24 px", not a bare number
+
+ var vm = SourceAssert.Text("ViewModels", "MainViewModel.Scoreboard.cs");
+ Assert.Contains("OnPropertyChanged(nameof(ScoreboardBackgroundOpacity));", vm);
+ Assert.Contains("OnPropertyChanged(nameof(ScoreboardFontSize));", vm);
+
// Dev-only labels are ORANGE, so a development affordance cannot pass for product.
Assert.Contains("Simulate (dev)", block);
Assert.Contains("Brush.Attention", block);
// Scroll speed: a real dial, persisted, with its range owned by the presenter.
Assert.Contains("ScoreboardScrollSpeed", block);
- Assert.Contains("Minimum=\"20\" Maximum=\"400\"", block);
+ Assert.Contains("Minimum=\"20\" Maximum=\"200\"", block);
Assert.Contains("Scoreboard.ScrollSpeed", SourceAssert.Text("Services", "LayoutStore.Settings.cs"));
// The master switch sits in the same panel: off hides the strip and stops the polling.
diff --git a/ytLive.Tests/ScoreboardRendererTests.cs b/ytLive.Tests/ScoreboardRendererTests.cs
index a972915..c20a0a6 100644
--- a/ytLive.Tests/ScoreboardRendererTests.cs
+++ b/ytLive.Tests/ScoreboardRendererTests.cs
@@ -17,6 +17,14 @@ public class ScoreboardRendererTests
[Fact]
public void The_strip_paints_the_plate_and_the_text_and_never_needs_a_scroll()
{
+ // The shipped defaults: white text, background ON, black, 20%.
+ var defaults = new ScoreboardStyle();
+ Assert.Equal("#FFFFFF", defaults.TextColor);
+ Assert.True(defaults.BackgroundEnabled);
+ Assert.Equal("#000000", defaults.BackgroundColor);
+ Assert.Equal(0.20, defaults.BackgroundOpacity, 3);
+ Assert.Equal(200, ytLive.Services.Compositor.ScoreboardPresenter.MaxPixelsPerSecond, 3);
+
Exception? failure = null;
var thread = new Thread(() =>