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(() =>