feat(35): scenes remember the room — BRB mutes the mic on entry
591 green, 0 warnings.
- `Scene.SceneMutesMic` / `SceneMutesDesktop` are **nullable booleans**: null = leave the mix alone, so
a scene is passive until the creator says otherwise. Schema columns added, NULL for every existing
layout except one deliberate default.
- **Applied once when the scene takes the PROGRAM output** (`TransitionToScene`, only when live or
recording). Staging a scene offline is auditioning, not a stream edit — the test asserts that.
- **The creator is boss:** a manual mute survives any transition into a scene with no opinion; a scene
that does have one wins on its next entry. `SetMicMuted`/`SetGameMuted` are idempotent so a repeated
"mute" cannot clobber the level that unmute would restore.
- **BRB ships muted** (creator spec) for new layouts *and* via a one-time migration for existing rows
where the creator has never expressed an opinion.
- UI: two three-state checkboxes under the layers list ("When this scene goes live"), styled with
`PropertyPillCheck` so the theme test holds.
`SceneAudioTests` covers the whole rule in one integration test: default state, offline staging does
nothing, live entry mutes, manual unmute survives a passive scene, an opinionated scene wins, desktop
untouched, and all three states roundtrip through save/reload.
Needs the creator's eye: the checkboxes are new chrome in the left panel (indeterminate = passive).
This commit is contained in:
+24
-5
@@ -101,6 +101,7 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
@@ -341,10 +342,28 @@
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
||||
|
||||
<!-- TASK 35: the scene remembers the room. Three states on purpose:
|
||||
checked = mute on entry, unchecked = unmute on entry, indeterminate = leave the
|
||||
mix alone (the default, so a scene is passive until it is told). -->
|
||||
<StackPanel Grid.Row="3" Margin="0,10,0,0"
|
||||
Visibility="{Binding StagedScene, Converter={StaticResource NotNullToVis}}">
|
||||
<TextBlock Text="WHEN THIS SCENE GOES LIVE" Style="{StaticResource SectionHeader}"/>
|
||||
<CheckBox Content="Mute the mic" IsThreeState="True" Margin="10,6,0,0"
|
||||
Style="{StaticResource PropertyPillCheck}"
|
||||
IsChecked="{Binding SceneMutesMic, Mode=TwoWay}"
|
||||
Foreground="{StaticResource Brush.OnSurface}" FontSize="12"
|
||||
ToolTip="Checked mutes on entry · Unchecked unmutes on entry · Indeterminate leaves the mix alone"/>
|
||||
<CheckBox Content="Mute desktop audio" IsThreeState="True" Margin="10,4,0,0"
|
||||
Style="{StaticResource PropertyPillCheck}"
|
||||
IsChecked="{Binding SceneMutesDesktop, Mode=TwoWay}"
|
||||
Foreground="{StaticResource Brush.OnSurface}" FontSize="12"
|
||||
ToolTip="Checked mutes on entry · Unchecked unmutes on entry · Indeterminate leaves the mix alone"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Resource alert: pinned to the bottom of the Layers panel.
|
||||
Red when a required resource (the one-and-only webcam)
|
||||
could not be locked; persists and re-polls until resolved. -->
|
||||
<Border Grid.Row="3" Margin="0,8,0,0" Padding="8,6" CornerRadius="4"
|
||||
<Border Grid.Row="4" Margin="0,8,0,0" Padding="8,6" CornerRadius="4"
|
||||
Background="{StaticResource Brush.ErrorContainer}" BorderBrush="{StaticResource Brush.Error}" BorderThickness="1"
|
||||
Visibility="{Binding WebcamLockAlert, Converter={StaticResource NotNullToVis}}">
|
||||
<StackPanel>
|
||||
@@ -358,17 +377,17 @@
|
||||
|
||||
<!-- ELEMENTS panel: per-element config beneath Sources.
|
||||
Collapses entirely when no element is selected. -->
|
||||
<Rectangle Grid.Row="4" Height="1" Fill="{StaticResource Brush.Outline}" Margin="0,22,0,10"/>
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="0,8,0,0">
|
||||
<Rectangle Grid.Row="5" Height="1" Fill="{StaticResource Brush.Outline}" Margin="0,22,0,10"/>
|
||||
<StackPanel Grid.Row="6" 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="{StaticResource Brush.Tertiary}" 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>
|
||||
<TextBlock Grid.Row="6" Text="Select an item to edit its properties" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="12"
|
||||
<TextBlock Grid.Row="7" Text="Select an item to edit its properties" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="12"
|
||||
Margin="8,4,0,0" TextWrapping="Wrap"
|
||||
Visibility="{Binding SelectedElement, Converter={StaticResource NullToVis}}"/>
|
||||
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="6" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="7" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
||||
<StackPanel x:Name="ElementsPanel" Margin="8,4,0,0">
|
||||
<StackPanel Visibility="{Binding SelectedElement.IsTextOverlay, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
||||
|
||||
Reference in New Issue
Block a user