Files
LlamaCasty/Controls/SettingsDrawer.xaml
gramps d46e84562f feat(settings): editing during a session is the creator's call (default: allowed)
Creator ruling 2026-11-12: "we don't care if a creator wants to edit their scene: move stuff around,
shrink/grow a webcam view, etc. Why not?" Correct — the app was only ever *forcing* EDIT off at
go-live, never enforcing it, so the gun-safety was a habit rather than a rule.

App.LockEditWhileLive ships off: a show no longer takes EDIT away. Turning it on restores the old
behaviour exactly — go-live flips EDIT off and stop flips it back, and only because the lock took it,
so a creator who had already turned EDIT off does not get it handed back on stop.

MainViewModel.ApplyEditLockForSession holds the rule so it is testable without starting a broadcast;
EditWhileLiveTests covers both settings, the round trip, and the "don't hand it back" case. Settings →
Editing carries the checkbox.

This is the first slice of TASK 68 (editing while live).

678 pass, 0 warnings. Scope justification: Assets/starting-background.jpg is outside this unit — the
creator's replacement art, still awaiting their visual check.
2026-11-12 14:45:58 -08:00

169 lines
13 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<UserControl x:Class="ytLive.Controls.SettingsDrawer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:models="clr-namespace:ytLive.Models"
xmlns:services="clr-namespace:ytLive.Services"
xmlns:notification="clr-namespace:Notification.Core;assembly=Notification.Core">
<!-- App Settings, as a RIGHT-EDGE PULL-OUT (creator ruling 2026-11-01): the gear tab opens
this drawer immediately, in the same animated-width pattern as Stream Settings / YPP /
Socials / TEST. It used to be a centred modal overlay, which then needed its own scroll
viewer once the section list outgrew the shell — a drawer has the height of the preview
and that problem disappears.
Categories are headings; their controls are indented under them (Zoom is one). -->
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="0,0,0,0">
<!-- ═══ RECORDINGS ═══ -->
<TextBlock Text="Recordings" Style="{StaticResource SectionHeader}" Margin="0,0,0,4"/>
<TextBlock Text="Default location" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="10,0,0,2"/>
<DockPanel Margin="10,0,0,10">
<Button DockPanel.Dock="Right" Content="Browse…" Command="{Binding ChooseRecordFolderCommand}"
Style="{StaticResource YtButtonSecondary}" Padding="8,2" FontSize="11" Margin="6,0,0,0"/>
<Button DockPanel.Dock="Right" Content="Use Downloads" Command="{Binding ResetRecordFolderCommand}"
Style="{StaticResource YtButtonSecondary}" Padding="8,2" FontSize="11" Margin="6,0,0,0"/>
<TextBox Style="{StaticResource YtTextBoxCompact}" Text="{Binding RecordFolderDisplay, Mode=OneWay}"
IsReadOnly="True" Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
</DockPanel>
<TextBlock Text="Slide-out alerts" Style="{StaticResource SectionHeader}" Margin="0,6,0,4"/>
<TextBlock Text="Position" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="10,0,0,2"/>
<ComboBox Style="{StaticResource YtComboBox}" Margin="10,0,0,8"
SelectedValue="{Binding ToastPosition, Mode=TwoWay}" SelectedValuePath="Tag"
ToolTip="Choose where notifications appear inside the app window.">
<ComboBoxItem Content="Top left" Tag="{x:Static notification:NotificationPosition.TopLeft}"/>
<ComboBoxItem Content="Top right" Tag="{x:Static notification:NotificationPosition.TopRight}"/>
<ComboBoxItem Content="Center left" Tag="{x:Static notification:NotificationPosition.CenterLeft}"/>
<ComboBoxItem Content="Center right" Tag="{x:Static notification:NotificationPosition.CenterRight}"/>
<ComboBoxItem Content="Bottom left" Tag="{x:Static notification:NotificationPosition.BottomLeft}"/>
<ComboBoxItem Content="Bottom right" Tag="{x:Static notification:NotificationPosition.BottomRight}"/>
</ComboBox>
<CheckBox Content="Keep alerts until dismissed" Style="{StaticResource PropertyPillCheck}"
Margin="12,2,0,2" IsChecked="{Binding StickyAlertsUntilDismissed, Mode=TwoWay}"
ToolTip="When on, every slide-out alert stays until you click it or close it."/>
<TextBlock Text="Off: informational alerts use their normal timeouts; errors still stay until closed."
Style="{StaticResource PropertyHint}" Margin="10,0,0,8"/>
<!-- ═══ BRAND ═══ -->
<TextBlock Text="Brand logo" Style="{StaticResource SectionHeader}" Margin="0,6,0,4"/>
<TextBlock Text="Image file (shown on every scene)" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="10,0,0,2"/>
<DockPanel Margin="10,0,0,10">
<Button DockPanel.Dock="Right" Content="Browse…" Command="{Binding BrowseBrandLogoCommand}"
Style="{StaticResource YtButtonSecondary}" Padding="8,2" FontSize="11" Margin="6,0,0,0"/>
<!-- A file NAME says nothing about the picture. The thumbnail shows what is set;
hovering enlarges it, a click collapses it again. -->
<Border x:Name="BrandLogoThumbFrame" DockPanel.Dock="Left" Width="28" Height="28"
Margin="0,0,6,0" CornerRadius="4" ClipToBounds="True"
Background="{StaticResource Brush.SurfaceContainerLowest}"
BorderBrush="{StaticResource Brush.Outline}" BorderThickness="1"
Cursor="Hand" ToolTip="Hover to enlarge — click to close"
Visibility="{Binding BrandLogoPreviewImage, Converter={StaticResource NotNullToVis}}">
<Image x:Name="BrandLogoThumbImage" Source="{Binding BrandLogoPreviewImage}"
Stretch="Uniform" MouseEnter="BrandLogoThumb_MouseEnter"
MouseLeave="BrandLogoThumb_MouseLeave"
MouseLeftButtonUp="BrandLogoThumb_Click"/>
</Border>
<TextBox Style="{StaticResource YtTextBoxCompact}" Text="{Binding BrandLogoFileDisplay, Mode=OneWay}"
IsReadOnly="True" Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
</DockPanel>
<Popup x:Name="BrandLogoPreviewPopup" PlacementTarget="{Binding ElementName=BrandLogoThumbFrame}"
Placement="Left" HorizontalOffset="-6" StaysOpen="True" AllowsTransparency="True"
IsHitTestVisible="True" MouseLeave="BrandLogoPreviewPopup_MouseLeave">
<Border Padding="6" CornerRadius="6"
Background="{StaticResource Brush.SurfaceContainerHighest}"
BorderBrush="{StaticResource Brush.Outline}" BorderThickness="1">
<StackPanel>
<Image Source="{Binding BrandLogoPreviewImage}" MaxWidth="420" MaxHeight="320"
Stretch="Uniform" RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Text="{Binding BrandLogoFileDisplay}" FontSize="11" Margin="0,6,0,0"
Foreground="{StaticResource Brush.OnSurfaceVariant}"/>
</StackPanel>
</Border>
</Popup>
<!-- ═══ ZOOM ═══ -->
<TextBlock Text="Clips" Style="{StaticResource SectionHeader}" Margin="0,10,0,4"/>
<CheckBox Content="Save replay clips" Style="{StaticResource PropertyPillCheck}" Margin="12,2,0,4"
IsChecked="{Binding ClipsEnabled, Mode=TwoWay}"
ToolTip="F10 saves the last few seconds of the show. Off by default; nothing runs while it is off."/>
<StackPanel Margin="12,0,0,4" IsEnabled="{Binding ClipsEnabled}">
<TextBlock Text="Clip length" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,0,0,2"/>
<ComboBox ItemsSource="{Binding ClipLengthOptions}" SelectedItem="{Binding ClipLengthText, Mode=TwoWay}"
Style="{StaticResource YtComboBox}" Height="28" Margin="0,0,0,6"/>
<TextBlock Text="Keep" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,0,0,2"/>
<ComboBox ItemsSource="{Binding ClipKeepOptions}" SelectedItem="{Binding ClipKeepText, Mode=TwoWay}"
Style="{StaticResource YtComboBox}" Height="28"/>
<TextBlock Text="A capture, not an editor: for trimming, captions or a library, use a video editor."
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="10" TextWrapping="Wrap" Margin="0,6,0,0"/>
</StackPanel>
<TextBlock Text="Zoom" Style="{StaticResource SectionHeader}" Margin="0,6,0,4"/>
<TextBlock Text="Zoom view shape" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="10,0,0,2"/>
<ComboBox Style="{StaticResource YtComboBox}" Margin="10,0,0,8" SelectedValue="{Binding ZoomShape}" SelectedValuePath="Tag">
<ComboBoxItem Content="Circle" Tag="{x:Static models:ZoomShape.Circle}"/>
<ComboBoxItem Content="Phone (Vertical Rectangle)" Tag="{x:Static models:ZoomShape.VerticalRectangle}"/>
<ComboBoxItem Content="Tablet (Horizontal Rectangle)" Tag="{x:Static models:ZoomShape.HorizontalRectangle}"/>
</ComboBox>
<DockPanel Margin="10,0,0,10">
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource PillToggle}"
IsChecked="{Binding ZoomFollowsBox, Mode=TwoWay}" VerticalAlignment="Center"
ToolTip="Off: the box magnifies the area you clicked. On: it magnifies whatever it hovers over."/>
<TextBlock Text="Zoom box follows" Foreground="{StaticResource Brush.OnSurface}" FontSize="11" VerticalAlignment="Center"/>
</DockPanel>
<!-- ═══ VIDEO CLIPS (TASK 64) ═══ -->
<TextBlock Text="Video clips" Style="{StaticResource SectionHeader}" Margin="0,6,0,4"/>
<DockPanel Margin="10,0,0,10">
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource PillToggle}"
IsChecked="{Binding AutoChromaScreen, Mode=TwoWay}" VerticalAlignment="Center"
ToolTip="On: a clip shot against a clear green or blue screen is keyed as soon as it is added. Off: the app asks first."/>
<StackPanel>
<TextBlock Text="Auto Green/Blue Screen" Foreground="{StaticResource Brush.OnSurface}" FontSize="11"/>
<TextBlock Text="Removes a detected screen without asking." Style="{StaticResource PropertyHint}"/>
</StackPanel>
</DockPanel>
<!-- ═══ SCENE TRANSITIONS ═══ -->
<TextBlock Text="Scene transition" Style="{StaticResource SectionHeader}" Margin="0,6,0,4"/>
<TextBlock Text="Type" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="10,0,0,2"/>
<ComboBox Style="{StaticResource YtComboBox}" Margin="10,0,0,8" SelectedValue="{Binding SelectedTransitionType}" SelectedValuePath="Tag">
<ComboBoxItem Content="Cut" Tag="{x:Static services:TransitionType.Cut}"/>
<ComboBoxItem Content="Fade" Tag="{x:Static services:TransitionType.Fade}"/>
<ComboBoxItem Content="Move" Tag="{x:Static services:TransitionType.Move}"/>
<ComboBoxItem Content="Wipe" Tag="{x:Static services:TransitionType.Wipe}"/>
</ComboBox>
<DockPanel Margin="10,0,0,10">
<TextBlock DockPanel.Dock="Right" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Margin="8,0,0,0" Width="56">
<Run Text="{Binding TransitionDurationMs, StringFormat={}{0:0}}"/><Run Text=" ms"/>
</TextBlock>
<Slider Minimum="100" Maximum="2000" TickFrequency="50" IsSnapToTickEnabled="True"
Value="{Binding TransitionDurationMs, Mode=TwoWay}" VerticalAlignment="Center"/>
</DockPanel>
<!-- ═══ LAYOUT DEFAULTS (TASK 37) ═══ -->
<TextBlock Text="Editing" Style="{StaticResource SectionHeader}" Margin="0,10,0,4"/>
<CheckBox IsChecked="{Binding LockEditWhileLive, Mode=TwoWay}" Margin="10,0,0,2"
Style="{StaticResource PropertyPillCheck}"
Content="Lock editing while recording or live"/>
<TextBlock Text="Off: layers can be moved or resized during a show. On: going live turns EDIT off and stopping turns it back on."
Style="{StaticResource PropertyHint}" Margin="28,0,0,10"/>
<TextBlock Text="Layout defaults" Style="{StaticResource SectionHeader}" Margin="0,10,0,4"/>
<TextBlock Text="Replaces every scene and layer with this build’s factory layout. Your selected webcam stays the same."
Style="{StaticResource PropertyHint}" Margin="10,0,0,6"/>
<Button Content="Restore staged scene" Command="{Binding RestoreStagedSceneDefaultsCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="10,0,0,4" HorizontalAlignment="Left"/>
<TextBlock Text="Resets only the scene currently staged in the preview."
Style="{StaticResource PropertyHint}" Margin="10,0,0,8"/>
<Button Content="Restore all scenes" Command="{Binding RestoreAllLayoutDefaultsCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="10,0,0,10" HorizontalAlignment="Left"/>
<!-- Development panel host. Empty in a Release build: the panel itself is excluded
from the Release compile, and the code-behind only fills this inside #if DEBUG. -->
<StackPanel x:Name="DeveloperPanelHost"/>
</StackPanel>
</ScrollViewer>
</UserControl>