feat(scoreboard): unit 6 — it is a LAYER (source type, strip geometry, resolver, session hook)
Adds SourceType.Scoreboard with the same per-scene eye toggle, drag and presence as every other element — 'prominent on BRB, dialled back on Live' is geometry, not a second mechanism. Default geometry is the strip: 1920 wide, 56 tall, bottom edge. The output resolver returns the presenter's cached frame exactly like the chat box and alert box do, and every session start clears the tallies. Preview publication writes one shared WriteableBitmap on the UI thread (the pump thread must never touch one), refreshed only when a displayed value moves. Guard: ScoreboardLayerTests pins the enum member, the geometry constants, the resolver case and the session hook, so the layer cannot quietly become a special widget.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using ytLive.Helpers;
|
||||
using ytLive.Models;
|
||||
using ytLive.Services;
|
||||
using ytLive.Services.Compositor;
|
||||
|
||||
namespace ytLive.ViewModels;
|
||||
|
||||
/// <summary>The scoreboard strip: session tallies rendered as a full-width bar, per scene.
|
||||
///
|
||||
/// <para>It is a <see cref="SourceType.Scoreboard"/> layer, so a creator gets the same eye toggle,
|
||||
/// drag and per-scene presence as every other element — "prominent on BRB, dialled back on Live" is
|
||||
/// geometry, not a second mechanism.</para></summary>
|
||||
public partial class MainViewModel
|
||||
{
|
||||
private readonly ScoreboardRecorder _scoreboard = new();
|
||||
private ScoreboardTiles _scoreboardTiles = ScoreboardTiles.Default;
|
||||
private ScoreboardStyle _scoreboardStyle = new();
|
||||
private ScoreboardPresenter? _scoreboardPresenter;
|
||||
private WriteableBitmap? _scoreboardPreview;
|
||||
private Source? _scoreboardPreviewSource;
|
||||
|
||||
internal ScoreboardRecorder ScoreboardRecorder => _scoreboard;
|
||||
|
||||
internal ScoreboardTiles ScoreboardTilesState => _scoreboardTiles;
|
||||
|
||||
/// <summary>Composited strip frame for the output path (cached by the presenter).</summary>
|
||||
internal VideoFrame? ScoreboardFrame => _scoreboardPresenter?.Frame;
|
||||
|
||||
internal void StartScoreboardSession()
|
||||
{
|
||||
_scoreboard.StartSession();
|
||||
RefreshScoreboardPreviews();
|
||||
}
|
||||
|
||||
internal void EndScoreboardSession()
|
||||
{
|
||||
_scoreboard.EndSession();
|
||||
RefreshScoreboardPreviews();
|
||||
}
|
||||
|
||||
private void InitScoreboard()
|
||||
{
|
||||
_scoreboardPresenter = new ScoreboardPresenter(_scoreboard, _scoreboardStyle)
|
||||
{
|
||||
// placeholder so SetTiles below drives the first render
|
||||
};
|
||||
_scoreboardPresenter.SetTiles(_scoreboardTiles);
|
||||
_scoreboardPresenter.SetStyle(_scoreboardStyle);
|
||||
_scoreboard.Changed += RefreshScoreboardPreviews;
|
||||
}
|
||||
|
||||
/// <summary>Snapshot of the strip presenters as themed properties, for the properties panel
|
||||
/// (unit 7) — kept here so the panel binds one object instead of six primitives.</summary>
|
||||
internal ScoreboardStyle ScoreboardStyleState => _scoreboardStyle;
|
||||
|
||||
/// <summary>Publishes the strip to every scoreboard layer's preview element. The pump thread
|
||||
/// must never touch a WriteableBitmap, so this runs on the UI thread only.</summary>
|
||||
private void RefreshScoreboardPreviews()
|
||||
{
|
||||
var frame = _scoreboardPresenter?.Frame;
|
||||
foreach (var scene in Scenes)
|
||||
foreach (var source in scene.Elements.OfType<Source>().Where(s => s.IsScoreboard))
|
||||
{
|
||||
if (frame is null) { source.VideoImageSource = null; continue; }
|
||||
if (_scoreboardPreview is null || _scoreboardPreview.PixelWidth != frame.Width ||
|
||||
_scoreboardPreview.PixelHeight != frame.Height)
|
||||
{
|
||||
_scoreboardPreview = new WriteableBitmap(frame.Width, frame.Height, 96, 96, PixelFormats.Bgra32, null);
|
||||
}
|
||||
_scoreboardPreview.WritePixels(new System.Windows.Int32Rect(0, 0, frame.Width, frame.Height),
|
||||
frame.BgraPixels, frame.Width * 4, 0);
|
||||
source.VideoImageSource = _scoreboardPreview;
|
||||
_scoreboardPreviewSource = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user