feat(alerts): add scene-local recent ticker

Model the AlertBox and standalone ticker with shared settings; scene tickers render the in-memory active and queued events without saving history. Keep AlertBox content active-event-only.

Ref: https://obsproject.com/kb/obs-studio-overview
This commit is contained in:
2026-11-02 10:47:48 -08:00
parent 6208cb9409
commit 20f059422b
20 changed files with 339 additions and 90 deletions
+29 -3
View File
@@ -82,6 +82,9 @@
<MenuItem Header="Stream Alerts" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.AlertBox}"
IsEnabled="{Binding CanAddAlerts}"
ToolTip="Celebration zone for Super Chat &amp; memberships — one alert layer at a time"/>
<MenuItem Header="Ticker" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.Ticker}"
IsEnabled="{Binding CanAddTicker}"
ToolTip="Recent alert feed — one independently configured ticker per scene"/>
<!-- Privacy blur: Live scene only (creator ruling 2026-11-01). Sits
above the desktop and below the brand logo; several allowed. -->
<MenuItem Header="Blur" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.Blur}"
@@ -219,6 +222,9 @@
<Path Data="M6,16 V11 A6,6 0 0 1 18,11 V16 L20,18 H4 Z M10,20 H14" Stroke="{StaticResource Brush.Tertiary}" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsAlertBox}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M3,6 H21 M3,12 H21 M3,18 H21" Stroke="{StaticResource Brush.Tertiary}" StrokeThickness="1.6" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsTicker}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
</Grid>
<TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center"
@@ -817,9 +823,9 @@
<Border Style="{StaticResource PropertyDivider}"/>
<TextBlock Text="Ticker" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Message ticker" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Current event" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ToggleButton Style="{StaticResource PillToggle}"
IsChecked="{Binding SelectedElement.AlertShowTicker, Mode=TwoWay}"
IsChecked="{Binding SelectedElement.Ticker.IsEnabled, Mode=TwoWay}"
HorizontalAlignment="Left"/>
</DockPanel>
@@ -828,7 +834,7 @@
same shape as the chat Font selector above. -->
<DockPanel Margin="10,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Display" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ComboBox SelectedValue="{Binding SelectedElement.AlertDisplayMethod, Mode=TwoWay}"
<ComboBox SelectedValue="{Binding SelectedElement.Ticker.DisplayMethod, Mode=TwoWay}"
SelectedValuePath="Tag"
Style="{StaticResource YtComboBox}"
Width="140" VerticalAlignment="Center">
@@ -847,6 +853,26 @@
</DockPanel>
</StackPanel>
<!-- Per-scene ticker layer: same Ticker settings/renderer as the
ticker embedded in Stream Alerts, but it reads the recent event feed. -->
<StackPanel Visibility="{Binding SelectedElement.IsTicker, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Recent Alert Ticker" Style="{StaticResource PropertyHeader}"/>
<TextBlock Text="Shows current and queued alert events. This ticker's settings are independent in each scene."
Style="{StaticResource PropertyHint}"/>
<CheckBox Content="Show recent alerts" Style="{StaticResource PropertyPillCheck}" Margin="0,2,0,4"
IsChecked="{Binding SelectedElement.Ticker.IsEnabled, Mode=TwoWay}"/>
<DockPanel Margin="10,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Display" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ComboBox SelectedValue="{Binding SelectedElement.Ticker.DisplayMethod, Mode=TwoWay}"
SelectedValuePath="Tag" Style="{StaticResource YtComboBox}"
Width="140" VerticalAlignment="Center">
<ComboBoxItem Content="Scroll" Tag="TickerScroll"/>
<ComboBoxItem Content="Flash" Tag="Flash"/>
<ComboBoxItem Content="Solid" Tag="Solid"/>
</ComboBox>
</DockPanel>
</StackPanel>
<!-- Background properties -->
<StackPanel Margin="0,8,0,0"
Visibility="{Binding SelectedElement.IsBackground, Converter={StaticResource BoolToVis}}">
+3
View File
@@ -152,6 +152,9 @@
<DataTrigger Binding="{Binding IsAlertBox}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsTicker}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<!-- The scoreboard strip is a frame-source too: without this trigger its
Image stays collapsed and every property change looks inert. -->
<DataTrigger Binding="{Binding IsScoreboard}" Value="True">
+4
View File
@@ -17,6 +17,10 @@ See [`schema.md`](../schema.md) for the memory-map conventions.
| `SceneThumbnailStrip.xaml.cs` | Code-behind for the strip above |
| `LeftPanel.xaml.cs` | Code-behind for the panel above |
The Add Layer menu exposes one standalone recent-alert Ticker per scene. The
properties panel edits its independent `Ticker` settings; Stream Alerts retains
its separate current-event ticker controls.
## Window ↔ control contract
The shell (`MainWindow.xaml(.cs)`) wires up each control and routes the few