Layers panel and scene strip: scene layers header with icons, per-type layer icons, PROGRAM badge and selected/live orange borders, scene name and hotkey overlays, Starting as default scene

- LeftPanel: SCENE LAYERS header with layer count; per-type blue icons; properties header gear; layers list min/max height; properties placeholder; list and properties indent
- SceneThumbnailStrip: names/hotkeys over the tile; PROGRAM badge with blinking red dot on the selected tile; orange border on selected and live tiles; fixed the element border override that hid the style border; reflag tiles on load
- LiveScene is now set on go-live, record start, and live transitions (it was never assigned, so IsLive never fired)
- Startup stages the scene named Starting
- DESIGN.md: color roles (red = YouTube/on-air/record, orange #ff8c3a = attention, amber = game audio/Super Chat)
- Staged live tile uses background art instead of the green LIVE placeholder

Not pushed.
This commit is contained in:
2026-10-26 14:00:23 -07:00
parent 24aca7d04a
commit 3239808a73
12 changed files with 146 additions and 45 deletions
+61 -15
View File
@@ -49,12 +49,19 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- LAYERS header + Add button -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,8,0,0">
<TextBlock Text="LAYERS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
<Path Data="M12,3 L21,8 L12,13 L3,8 Z M3,12 L12,17 L21,12 M3,16 L12,21 L21,16"
Stroke="#38bdf8" StrokeThickness="1.8" StrokeLineJoin="Round"
Width="16" Height="16" Stretch="Uniform" VerticalAlignment="Center" Margin="0,-4,6,0"/>
<TextBlock Text="SCENE LAYERS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
<Border Width="20" Height="20" CornerRadius="10" Background="#334155" Margin="6,-4,0,0" VerticalAlignment="Center">
<TextBlock Text="{Binding LayerCount}" Foreground="#38bdf8" FontSize="11" FontWeight="SemiBold"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Button ToolTip="Add Layer" Style="{StaticResource IconButton}" Margin="4,-4,0,0"
Click="AddSourceButton_Click">
<Button.ContextMenu>
@@ -82,7 +89,7 @@
</Button>
</StackPanel>
<Helpers:FocusPreservingListBox x:Name="SourceList" Grid.Row="1" Background="Transparent" BorderThickness="0"
<Helpers:FocusPreservingListBox x:Name="SourceList" Grid.Row="1" MinHeight="84" MaxHeight="240" Background="Transparent" BorderThickness="0"
ItemsSource="{Binding StagedScene.Elements}"
SelectedItem="{Binding SelectedElement, Mode=TwoWay}"
PreviewMouseLeftButtonDown="List_PreviewMouseLeftButtonDown"
@@ -106,15 +113,17 @@
</Helpers:FocusPreservingListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2"
<Grid Margin="8,2,0,2"
Tag="{Binding DataContext.CanChangeBackground, RelativeSource={RelativeSource AncestorType=Window}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Change Capture…" Click="BackgroundMenu_ChangeCapture">
@@ -171,7 +180,40 @@
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"
<Grid Grid.Column="0" Width="16" Height="16" Margin="0,0,6,0" VerticalAlignment="Center">
<Path Data="M7,8 H17 A4,4 0 0 1 21,12 V14 A3,3 0 0 1 17,16 L15,14 H9 L7,16 A3,3 0 0 1 3,14 V12 A4,4 0 0 1 7,8 Z M8,11 V13 M7,12 H9 M15,11 H15.01 M17,13 H17.01" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsLiveCapture}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M3,5 H21 V19 H3 Z M3,16 L9,10 L13,14 L16,11 L21,16" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><MultiDataTrigger><MultiDataTrigger.Conditions><Condition Binding="{Binding IsBackground}" Value="True"/><Condition Binding="{Binding IsLiveCapture}" Value="False"/></MultiDataTrigger.Conditions><Setter Property="Visibility" Value="Visible"/></MultiDataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M3,7 H15 V17 H3 Z M15,11 L21,8 V16 L15,13" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsWebcam}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M4,4 H20 V20 H4 Z M4,16 L9,11 L13,15 L16,12 L20,16 M15,8 H15.01" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsImageSource}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M5,5 H19 M12,5 V19 M8,19 H16" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsTextOverlay}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M12,3 A9,9 0 1 0 12.01,3 M12,7 V12 L15,14" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsCountdown}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M12,3 A9,9 0 1 0 12.01,3 M3,12 H21 M12,3 C8,7 8,17 12,21 M12,3 C16,7 16,17 12,21" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsWebSource}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M4,5 H20 V16 H10 L6,20 V16 H4 Z" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsChatBox}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M4,5 H20 V19 H4 Z M10,9 L15,12 L10,15 Z" Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round" Stretch="Uniform">
<Path.Style><Style TargetType="Path"><Setter Property="Visibility" Value="Collapsed"/><Style.Triggers><DataTrigger Binding="{Binding IsMediaSource}" Value="True"><Setter Property="Visibility" Value="Visible"/></DataTrigger></Style.Triggers></Style></Path.Style>
</Path>
<Path Data="M6,16 V11 A6,6 0 0 1 18,11 V16 L20,18 H4 Z M10,20 H14" Stroke="#38bdf8" 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>
</Grid>
<TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center"
Padding="4,2" TextTrimming="CharacterEllipsis">
<TextBlock.Style>
<Style TargetType="TextBlock">
@@ -185,7 +227,7 @@
</TextBlock.Style>
</TextBlock>
<TextBox Grid.Column="0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
<TextBox Grid.Column="1" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
BorderThickness="0" FontSize="12"
IsVisibleChanged="SourceNameBox_IsVisibleChanged"
KeyDown="SourceNameBox_KeyDown" LostFocus="SourceNameBox_LostFocus">
@@ -201,21 +243,21 @@
</TextBox.Style>
</TextBox>
<Button Grid.Column="1" ToolTip="Edit Layer Name"
<Button Grid.Column="2" ToolTip="Edit Layer Name"
Command="{Binding DataContext.EditElementCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" Style="{StaticResource IconButton}">
<Path Data="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"
Fill="#d0d0d0" Width="13" Height="13" Stretch="Uniform"/>
</Button>
<Button Grid.Column="2" Style="{StaticResource EyeButton}"
<Button Grid.Column="3" Style="{StaticResource EyeButton}"
Command="{Binding DataContext.ToggleElementVisibilityCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}">
<Path Style="{StaticResource EyeIconStyle}" Fill="#d0d0d0"
Width="13" Height="13" Stretch="Uniform"/>
</Button>
<Button Grid.Column="3" ToolTip="Remove Layer"
<Button Grid.Column="4" ToolTip="Remove Layer"
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" Style="{StaticResource IconButton}"
Visibility="{Binding IsBackground, Converter={StaticResource HiddenBoolToVis}}">
@@ -248,15 +290,19 @@
<!-- ELEMENTS panel: per-element config beneath Sources.
Collapses entirely when no element is selected. -->
<Rectangle Grid.Row="4" Height="1" Fill="#2c3a5c" Margin="0,10,0,2"
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}"/>
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="0,8,0,0"
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
<Rectangle Grid.Row="4" Height="1" Fill="#2c3a5c" Margin="0,22,0,10"/>
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="0,8,0,0">
<Path Data="M12,8 A4,4 0 1 0 12.01,8 M19.4,15 L21,16.2 L19.4,19 L17.2,18.3 A7,7 0 0 1 15,19.4 L14.5,21.5 H9.5 L9,19.4 A7,7 0 0 1 6.8,18.3 L4.6,19 L3,16.2 L4.6,15 A7,7 0 0 1 4.6,9 L3,7.8 L4.6,5 L6.8,5.7 A7,7 0 0 1 9,4.6 L9.5,2.5 H14.5 L15,4.6 A7,7 0 0 1 17.2,5.7 L19.4,5 L21,7.8 L19.4,9 A7,7 0 0 1 19.4,15 Z"
Stroke="#38bdf8" StrokeThickness="1.6" StrokeLineJoin="Round"
Width="16" Height="16" Stretch="Uniform" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="PROPERTIES" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
</StackPanel>
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="6" VerticalScrollBarVisibility="Auto" Background="Transparent"
<TextBlock Grid.Row="6" Text="Select an item to edit its properties" Foreground="#94a3b8" FontSize="12"
Margin="8,4,0,0" TextWrapping="Wrap"
Visibility="{Binding SelectedElement, Converter={StaticResource NullToVis}}"/>
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="6" MaxHeight="420" VerticalScrollBarVisibility="Auto" Background="Transparent"
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
<StackPanel x:Name="ElementsPanel" Margin="0,4,0,0">
<StackPanel x:Name="ElementsPanel" Margin="8,4,0,0">
<!-- Opacity + mirror/shape (draggable elements: image, webcam) -->
<StackPanel Visibility="{Binding SelectedElement.IsDraggable, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<StackPanel Visibility="{Binding SelectedElement.IsChatBox, Converter={StaticResource InverseBoolToVis}}">