- 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.
775 lines
67 KiB
XML
775 lines
67 KiB
XML
<UserControl x:Class="ytLive.Controls.LeftPanel"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"
|
|
xmlns:models="clr-namespace:ytLive.Models">
|
|
<UserControl.Resources>
|
|
<!-- TASK 63: the border-colour drop-down is the app's own YtComboBox, so the
|
|
colour entries are drawn by TEMPLATES, in the theme's colours and with the
|
|
theme's HexToBrush converter — no hand-built menu, no hand-painted brush.
|
|
The closed box shows the swatch + the colour's NAME; the rows show the
|
|
swatch + the hex + the name. -->
|
|
<DataTemplate x:Key="BorderColorClosedTemplate">
|
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
<Border Width="12" Height="12" CornerRadius="2" VerticalAlignment="Center"
|
|
Background="{Binding Hex, Converter={StaticResource HexToBrush}}"
|
|
BorderBrush="#2a3a5e" BorderThickness="1"/>
|
|
<TextBlock Text="{Binding Name}" Margin="6,0,0,0" FontSize="12"
|
|
Foreground="#e0e0e0" VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
<DataTemplate x:Key="BorderColorRowTemplate">
|
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
<Border Width="12" Height="12" CornerRadius="2" VerticalAlignment="Center"
|
|
Background="{Binding Hex, Converter={StaticResource HexToBrush}}"
|
|
BorderBrush="#2a3a5e" BorderThickness="1"/>
|
|
<TextBlock Text="{Binding Hex}" Margin="6,0,0,0" FontSize="12"
|
|
Foreground="#e0e0e0" VerticalAlignment="Center"/>
|
|
<TextBlock Text="{Binding Name}" Margin="8,0,0,0" FontSize="12"
|
|
Foreground="#a0a0b0" VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
<Helpers:ClosedBoxTemplateSelector x:Key="BorderColorTemplates"
|
|
ClosedTemplate="{StaticResource BorderColorClosedTemplate}"
|
|
RowTemplate="{StaticResource BorderColorRowTemplate}"/>
|
|
</UserControl.Resources>
|
|
|
|
<Border Background="#16213e" CornerRadius="6" Padding="12">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Edit mode (offline): layers + properties -->
|
|
<Grid Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BoolToVis}}">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- LAYERS header + Add button -->
|
|
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,8,0,0">
|
|
<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>
|
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
|
<MenuItem Header="Webcam" Command="{Binding AddWebcamCommand}"
|
|
IsEnabled="{Binding CanAddWebcam}"
|
|
ToolTip="{Binding WebcamAddToolTip}"/>
|
|
<MenuItem Header="Image" Command="{Binding AddImageCommand}"/>
|
|
<MenuItem Header="Text" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.TextOverlay}"/>
|
|
<MenuItem Header="Countdown" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.Countdown}"
|
|
Visibility="{Binding IsCountdownAvailable, Converter={StaticResource BoolToVis}}"
|
|
ToolTip="Timer overlay — Starting and BRB screens only"/>
|
|
<MenuItem Header="Web Resource" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.WebSource}"
|
|
ToolTip="Embedded web page — set URI in Properties panel"/>
|
|
<MenuItem Header="YouTube Chat" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.ChatBox}"
|
|
IsEnabled="{Binding CanAddYouTubeChat}"
|
|
ToolTip="One chat layer at a time — it's already in your stream"/>
|
|
<MenuItem Header="Stream Alerts" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.AlertBox}"
|
|
IsEnabled="{Binding CanAddAlerts}"
|
|
ToolTip="Celebration zone for Super Chat & memberships — one alert layer at a time"/>
|
|
</ContextMenu>
|
|
</Button.ContextMenu>
|
|
<Path Data="M12,4 L12,20 M4,12 L20,12" Stroke="#e94560" StrokeThickness="2"
|
|
Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
</StackPanel>
|
|
|
|
<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"
|
|
PreviewMouseMove="List_PreviewMouseMove"
|
|
PreviewMouseLeftButtonUp="List_PreviewMouseLeftButtonUp"
|
|
Foreground="#e0e0e0" FontSize="13">
|
|
<Helpers:FocusPreservingListBox.ItemContainerStyle>
|
|
<Style TargetType="ListBoxItem" BasedOn="{StaticResource YtListBoxItem}">
|
|
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsVisible}" Value="False">
|
|
<Setter Property="Opacity" Value="0.45"/>
|
|
</DataTrigger>
|
|
<!-- The transient zoom layer has no editing UI — never
|
|
show it as a layer row (TASK 51). -->
|
|
<DataTrigger Binding="{Binding IsZoomLayer}" Value="True">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Helpers:FocusPreservingListBox.ItemContainerStyle>
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<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">
|
|
<MenuItem.Visibility>
|
|
<MultiBinding Converter="{StaticResource AllTrueToVis}">
|
|
<Binding Path="IsBackground"/>
|
|
<Binding Path="PlacementTarget.Tag" RelativeSource="{RelativeSource AncestorType={x:Type ContextMenu}}"/>
|
|
</MultiBinding>
|
|
</MenuItem.Visibility>
|
|
</MenuItem>
|
|
<MenuItem Header="Refresh Capture" Click="BackgroundMenu_RefreshCapture">
|
|
<MenuItem.Visibility>
|
|
<MultiBinding Converter="{StaticResource AllTrueToVis}">
|
|
<Binding Path="IsBackground"/>
|
|
<Binding Path="PlacementTarget.Tag" RelativeSource="{RelativeSource AncestorType={x:Type ContextMenu}}"/>
|
|
</MultiBinding>
|
|
</MenuItem.Visibility>
|
|
</MenuItem>
|
|
<MenuItem Header="Capture Window…"
|
|
ItemsSource="{Binding DataContext.OpenWindows, RelativeSource={RelativeSource AncestorType=Window}}">
|
|
<MenuItem.Visibility>
|
|
<MultiBinding Converter="{StaticResource AllTrueToVis}">
|
|
<Binding Path="IsBackground"/>
|
|
<Binding Path="PlacementTarget.Tag" RelativeSource="{RelativeSource AncestorType={x:Type ContextMenu}}"/>
|
|
</MultiBinding>
|
|
</MenuItem.Visibility>
|
|
<MenuItem.ItemContainerStyle>
|
|
<Style TargetType="MenuItem">
|
|
<Setter Property="Header" Value="{Binding Label}"/>
|
|
<Setter Property="Command"
|
|
Value="{Binding DataContext.CaptureWindowCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
|
<Setter Property="CommandParameter" Value="{Binding}"/>
|
|
</Style>
|
|
</MenuItem.ItemContainerStyle>
|
|
</MenuItem>
|
|
<MenuItem Header="Refresh Window List" Click="BackgroundMenu_RefreshWindowList">
|
|
<MenuItem.Visibility>
|
|
<MultiBinding Converter="{StaticResource AllTrueToVis}">
|
|
<Binding Path="IsBackground"/>
|
|
<Binding Path="PlacementTarget.Tag" RelativeSource="{RelativeSource AncestorType={x:Type ContextMenu}}"/>
|
|
</MultiBinding>
|
|
</MenuItem.Visibility>
|
|
</MenuItem>
|
|
<Separator>
|
|
<Separator.Visibility>
|
|
<MultiBinding Converter="{StaticResource AllTrueToVis}">
|
|
<Binding Path="IsBackground"/>
|
|
<Binding Path="PlacementTarget.Tag" RelativeSource="{RelativeSource AncestorType={x:Type ContextMenu}}"/>
|
|
</MultiBinding>
|
|
</Separator.Visibility>
|
|
</Separator>
|
|
<MenuItem Header="Remove Layer" Click="SourceMenu_Remove"
|
|
Visibility="{Binding IsBackground, Converter={StaticResource InverseBoolToVis}}"/>
|
|
</ContextMenu>
|
|
</Grid.ContextMenu>
|
|
|
|
<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">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsEditing}" Value="True">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
|
|
<TextBox Grid.Column="1" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
|
|
BorderThickness="0" FontSize="12"
|
|
IsVisibleChanged="SourceNameBox_IsVisibleChanged"
|
|
KeyDown="SourceNameBox_KeyDown" LostFocus="SourceNameBox_LostFocus">
|
|
<TextBox.Style>
|
|
<Style TargetType="TextBox" BasedOn="{StaticResource YtTextBoxCompact}">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsEditing}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBox.Style>
|
|
</TextBox>
|
|
|
|
<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="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="4" ToolTip="Remove Layer"
|
|
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
|
CommandParameter="{Binding}" Style="{StaticResource IconButton}"
|
|
Visibility="{Binding IsBackground, Converter={StaticResource HiddenBoolToVis}}">
|
|
<Path Data="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"
|
|
Fill="#d0d0d0" Width="13" Height="13" Stretch="Uniform"/>
|
|
</Button>
|
|
</Grid>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</Helpers:FocusPreservingListBox>
|
|
|
|
<TextBlock Grid.Row="2" Text="No layers yet" Foreground="#555" FontSize="12"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
|
|
|
<!-- 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"
|
|
Background="#3a1216" BorderBrush="#e94560" BorderThickness="1"
|
|
Visibility="{Binding WebcamLockAlert, Converter={StaticResource NotNullToVis}}">
|
|
<StackPanel>
|
|
<TextBlock Text="{Binding WebcamLockAlert}" Foreground="#ffb4b4" FontSize="11"
|
|
TextWrapping="Wrap"/>
|
|
<Button Content="Retry" Command="{Binding RetryWebcamLockCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" Padding="10,2"
|
|
HorizontalAlignment="Right" Margin="0,6,0,0"/>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- ELEMENTS panel: per-element config beneath Sources.
|
|
Collapses entirely when no element is selected. -->
|
|
<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>
|
|
<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="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}}">
|
|
<TextBlock Text="Opacity" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"/>
|
|
<DockPanel Margin="0,0,0,6">
|
|
<TextBlock x:Name="OpacityValueText" Text="100%" Foreground="#a0a0b0" FontSize="11"
|
|
DockPanel.Dock="Right" VerticalAlignment="Center" Margin="8,0,0,0" Width="32"/>
|
|
<Slider x:Name="OpacitySlider" Width="120" Minimum="0" Maximum="1"
|
|
Value="{Binding SelectedElement.Opacity, Mode=TwoWay}"
|
|
ValueChanged="OpacitySlider_ValueChanged" VerticalAlignment="Center"/>
|
|
</DockPanel>
|
|
</StackPanel>
|
|
<!-- TASK 59: the shape is a CHOICE (Rect | Circle) under a
|
|
"Shape" title, with Rect selected by default — the circle
|
|
is not offered in the vertical Chat layout, so that option
|
|
is not even shown there (CanToggleClipShape). -->
|
|
<StackPanel Visibility="{Binding IsWebcamSelected, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Shape" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,4"/>
|
|
<StackPanel Orientation="Horizontal" Margin="10,0,0,4">
|
|
<Button Content="{Binding SelectedElement.MirrorButtonText}"
|
|
Style="{StaticResource YtButtonSecondary}" Padding="10,4"
|
|
Click="MirrorButton_Click"/>
|
|
<ToggleButton Style="{StaticResource SegmentToggle}" Padding="10,4" Margin="8,0,0,0"
|
|
IsChecked="{Binding SelectedElement.IsRectShape, Mode=TwoWay}">
|
|
<TextBlock Text="Rect" Style="{StaticResource SegmentLabel}"/>
|
|
</ToggleButton>
|
|
<ToggleButton Style="{StaticResource SegmentToggle}" Padding="10,4" Margin="4,0,0,0"
|
|
IsChecked="{Binding SelectedElement.IsRoundShape, Mode=TwoWay}"
|
|
Visibility="{Binding CanToggleClipShape, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Circle" Style="{StaticResource SegmentLabel}"/>
|
|
</ToggleButton>
|
|
</StackPanel>
|
|
</StackPanel>
|
|
<!-- The drag hint sits under the shape row — indented, so the
|
|
parentage shows. -->
|
|
<TextBlock Text="Drag to move · drag the corner dot to resize"
|
|
Foreground="#a0a0b0" FontSize="10" Margin="10,2,0,0"/>
|
|
</StackPanel>
|
|
|
|
<!-- Webcam geometry (TASK 57): under the vertical tier the camera
|
|
keeps its OWN placement, so these fields edit the LIVE one —
|
|
the placement the current tier shows. Gated on the VM's
|
|
IsWebcamSelected (the same condition as the mirror/shape row
|
|
above) so the webcam's border/background section stays the
|
|
one panel bound to SelectedElement.IsWebcam. -->
|
|
<StackPanel Visibility="{Binding IsWebcamSelected, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
|
<TextBlock Text="Geometry" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,4"/>
|
|
<!-- TASK 61: the four fields share ONE fixed width — wide enough
|
|
for the largest geometry value (1920, plus a typed sign) and
|
|
no wider, so Y/H no longer stretch to fill the row. -->
|
|
<DockPanel Margin="10,0,0,4" LastChildFill="False">
|
|
<TextBlock DockPanel.Dock="Left" Text="X" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="16"/>
|
|
<TextBox Text="{Binding SelectedElement.X, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
|
|
<TextBlock DockPanel.Dock="Left" Text=" Y" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
|
|
<TextBox Text="{Binding SelectedElement.Y, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
|
|
</DockPanel>
|
|
<DockPanel Margin="10,0,0,4" LastChildFill="False">
|
|
<TextBlock DockPanel.Dock="Left" Text="W" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="16"/>
|
|
<TextBox Text="{Binding SelectedElement.Width, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
|
|
<TextBlock DockPanel.Dock="Left" Text=" H" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
|
|
<TextBox Text="{Binding SelectedElement.Height, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
|
|
</DockPanel>
|
|
</StackPanel>
|
|
|
|
<!-- Webcam border config -->
|
|
<StackPanel Visibility="{Binding SelectedElement.IsWebcam, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
|
<TextBlock Text="Border Color" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,2"/>
|
|
<!-- TASK 63: the border colour is picked with the app's OWN
|
|
drop-down (YtComboBox) — the same widget the Display
|
|
and Font pickers use — not a hand-rolled menu. The hex
|
|
field stays for a colour the creator types; the
|
|
drop-down carries the colour's name. -->
|
|
<DockPanel Margin="10,0,0,6">
|
|
<TextBox DockPanel.Dock="Left"
|
|
Text="{Binding SelectedElement.BorderColor, UpdateSourceTrigger=PropertyChanged}"
|
|
Style="{StaticResource YtTextBoxCompact}"
|
|
MaxLength="7" ToolTip="#RRGGBB" Width="60"
|
|
VerticalAlignment="Center"/>
|
|
<ComboBox ItemsSource="{Binding SelectedElement.BorderColorOptions}"
|
|
SelectedItem="{Binding SelectedElement.BorderColorEntry, Mode=TwoWay}"
|
|
ItemTemplateSelector="{StaticResource BorderColorTemplates}"
|
|
Style="{StaticResource YtComboBox}"
|
|
ToolTip="Border colour" MinWidth="100"
|
|
Margin="8,0,0,0" VerticalAlignment="Center"/>
|
|
</DockPanel>
|
|
<TextBlock Text="Border Thickness" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,2"/>
|
|
<DockPanel Margin="10,0,0,6">
|
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
|
Width="22" Height="22" Margin="4,0,0,0"
|
|
Click="WebcamBorderAccept_Click">
|
|
<Path Data="M4,9 L8,14 L16,4" Stroke="#22c55e" StrokeThickness="2"
|
|
Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
|
Width="22" Height="22" ToolTip="Revert"
|
|
Click="WebcamBorderRevert_Click" Margin="4,0,0,0">
|
|
<Path Data="M5,5 L15,15 M15,5 L5,15" Stroke="#e94560" StrokeThickness="2"
|
|
Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
<Slider DockPanel.Dock="Left" Width="120" Minimum="0" Maximum="10"
|
|
Value="{Binding SelectedElement.BorderWidth, Mode=TwoWay}"
|
|
VerticalAlignment="Center" TickFrequency="1" IsSnapToTickEnabled="True"/>
|
|
<TextBlock Text="{Binding SelectedElement.BorderWidth}" Foreground="#a0a0b0" FontSize="11"
|
|
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
|
|
</DockPanel>
|
|
|
|
<!-- TASK 51: background effects — two pills acting as radio buttons
|
|
(one on turns the other off; both off = no effect). TASK 59:
|
|
the pills are indented under the Background title, and the
|
|
strength row one level deeper, under Blur. -->
|
|
<TextBlock Text="Background" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,6"/>
|
|
<DockPanel Margin="10,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Remove" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="84"/>
|
|
<ToggleButton x:Name="RemoveBackgroundPill" Style="{StaticResource PillToggle}"
|
|
IsChecked="{Binding SelectedElement.RemoveBackground, Mode=TwoWay}"
|
|
AutomationProperties.Name="Remove background"
|
|
ToolTip="Remove the background behind you — the layers below show through"
|
|
HorizontalAlignment="Left"/>
|
|
</DockPanel>
|
|
<DockPanel Margin="10,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Blur" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="84"/>
|
|
<ToggleButton x:Name="BlurBackgroundPill" Style="{StaticResource PillToggle}"
|
|
IsChecked="{Binding SelectedElement.BlurBackground, Mode=TwoWay}"
|
|
AutomationProperties.Name="Blur background"
|
|
ToolTip="Blur the background behind you"
|
|
HorizontalAlignment="Left"/>
|
|
</DockPanel>
|
|
<DockPanel Margin="20,0,0,6"
|
|
Visibility="{Binding SelectedElement.HasBackgroundEffect, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock DockPanel.Dock="Left" Text="Strength" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="84"/>
|
|
<TextBlock DockPanel.Dock="Right" Text="{Binding SelectedElement.BackgroundStrength, StringFormat={}{0:P0}}"
|
|
Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Margin="8,0,0,0" Width="34"/>
|
|
<Slider x:Name="BackgroundStrengthSlider" Minimum="0" Maximum="1"
|
|
Value="{Binding SelectedElement.BackgroundStrength, Mode=TwoWay}"
|
|
TickFrequency="0.05" IsSnapToTickEnabled="True" VerticalAlignment="Center"
|
|
AutomationProperties.Name="Background effect strength"
|
|
ToolTip="Blur: how strong the blur is · Remove: how soft the edge is"/>
|
|
</DockPanel>
|
|
</StackPanel>
|
|
|
|
<!-- Countdown config -->
|
|
<StackPanel Visibility="{Binding SelectedElement.IsCountdown, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
|
<TextBlock Text="Timer (minutes)" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"/>
|
|
<DockPanel Margin="0,0,0,6">
|
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
|
Width="22" Height="22" Margin="4,0,0,0"
|
|
Click="CountdownAccept_Click">
|
|
<Path Data="M4,9 L8,14 L16,4" Stroke="#22c55e" StrokeThickness="2"
|
|
Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
|
Width="22" Height="22" ToolTip="Revert"
|
|
Click="CountdownRevert_Click" Margin="4,0,0,0">
|
|
<Path Data="M5,5 L15,15 M15,5 L5,15" Stroke="#e94560" StrokeThickness="2"
|
|
Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
<Slider DockPanel.Dock="Left" Width="120" Minimum="1" Maximum="60"
|
|
Value="{Binding SelectedElement.TimerMinutes, Mode=TwoWay}"
|
|
VerticalAlignment="Center" TickFrequency="1" IsSnapToTickEnabled="True"/>
|
|
<TextBlock Text="{Binding SelectedElement.TimerMinutes}" Foreground="#a0a0b0" FontSize="11"
|
|
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
|
|
</DockPanel>
|
|
</StackPanel>
|
|
|
|
<!-- Web source config -->
|
|
<StackPanel Visibility="{Binding SelectedElement.IsWebSource, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
|
<TextBlock Text="Geometry" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,4"/>
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="X" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="16"/>
|
|
<TextBox Text="{Binding SelectedElement.X, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}"/>
|
|
<TextBlock DockPanel.Dock="Left" Text=" Y" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
|
|
<TextBox Text="{Binding SelectedElement.Y, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}"/>
|
|
</DockPanel>
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="W" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="16"/>
|
|
<TextBox Text="{Binding SelectedElement.Width, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}"/>
|
|
<TextBlock DockPanel.Dock="Left" Text=" H" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
|
|
<TextBox Text="{Binding SelectedElement.Height, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
|
|
Style="{StaticResource YtTextBoxCompact}"/>
|
|
</DockPanel>
|
|
<TextBlock Text="URI" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"/>
|
|
<DockPanel>
|
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
|
Width="22" Height="22" Margin="4,0,0,0" IsTabStop="False"
|
|
Click="WebAccept_Click">
|
|
<Path Data="M4,9 L8,14 L16,4" Stroke="#22c55e" StrokeThickness="2"
|
|
Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
|
Width="22" Height="22" ToolTip="Revert" IsTabStop="False"
|
|
Click="WebRevert_Click" Margin="4,0,0,0">
|
|
<Path Data="M5,5 L15,15 M15,5 L5,15" Stroke="#e94560" StrokeThickness="2"
|
|
Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
<TextBox Text="{Binding SelectedElement.WebUri, UpdateSourceTrigger=PropertyChanged}"
|
|
Style="{StaticResource YtTextBoxCompact}"
|
|
ToolTip="https://example.com"/>
|
|
</DockPanel>
|
|
</StackPanel>
|
|
|
|
<!-- Chat box config -->
|
|
<StackPanel Visibility="{Binding SelectedElement.IsChatBox, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
|
<TextBlock Text="Chat Overlay" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,6"/>
|
|
|
|
<!-- Font Size -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Font Size" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<Slider x:Name="ChatFontSizeSlider" Width="120" Minimum="8" Maximum="72" TickFrequency="2" IsSnapToTickEnabled="True"
|
|
Value="{Binding SelectedElement.ChatFontSize, Mode=TwoWay}"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock DockPanel.Dock="Right" Text="{Binding SelectedElement.ChatFontSize, StringFormat={}{0:0}}" Foreground="#e0e0e0" FontSize="11" Margin="6,0,0,0" VerticalAlignment="Center" Width="24"/>
|
|
</DockPanel>
|
|
|
|
<!-- Font Family -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Font" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<ComboBox SelectedValue="{Binding SelectedElement.ChatFontFamily, Mode=TwoWay}"
|
|
SelectedValuePath="Content"
|
|
Style="{StaticResource YtComboBox}"
|
|
Width="140" VerticalAlignment="Center">
|
|
<ComboBoxItem Content="Segoe UI"/>
|
|
<ComboBoxItem Content="Arial"/>
|
|
<ComboBoxItem Content="Consolas"/>
|
|
<ComboBoxItem Content="Courier New"/>
|
|
<ComboBoxItem Content="Comic Sans MS"/>
|
|
<ComboBoxItem Content="Mouse Memoirs"/>
|
|
</ComboBox>
|
|
</DockPanel>
|
|
|
|
<!-- Font Color -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Font Color" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<TextBox Text="{Binding SelectedElement.ChatFontColor, UpdateSourceTrigger=PropertyChanged}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
|
|
</DockPanel>
|
|
|
|
<!-- Member Color -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Member" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<TextBox Text="{Binding SelectedElement.ChatMemberColor, UpdateSourceTrigger=PropertyChanged}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
|
|
</DockPanel>
|
|
|
|
<!-- Super Chat Color -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Super Chat" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<TextBox Text="{Binding SelectedElement.ChatSuperChatColor, UpdateSourceTrigger=PropertyChanged}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
|
|
</DockPanel>
|
|
|
|
<!-- Max Messages -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Max Msgs" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<Slider x:Name="ChatMaxMsgsSlider" Width="120" Minimum="1" Maximum="20" TickFrequency="1" IsSnapToTickEnabled="True"
|
|
Value="{Binding SelectedElement.ChatMaxMessages, Mode=TwoWay}"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock DockPanel.Dock="Right" Text="{Binding SelectedElement.ChatMaxMessages}" Foreground="#e0e0e0" FontSize="11" Margin="6,0,0,0" VerticalAlignment="Center" Width="24"/>
|
|
</DockPanel>
|
|
|
|
<!-- Background Color -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Background" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<TextBox Text="{Binding SelectedElement.ChatBackgroundColor, UpdateSourceTrigger=PropertyChanged}"
|
|
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
|
|
</DockPanel>
|
|
|
|
<!-- Background Opacity -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Bg Opacity" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<Slider x:Name="ChatBgOpacitySlider" Width="120" Minimum="0" Maximum="1"
|
|
Value="{Binding SelectedElement.ChatBackgroundOpacity, Mode=TwoWay}"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock DockPanel.Dock="Right" Text="{Binding SelectedElement.ChatBackgroundOpacity, StringFormat={}{0:0%}}" Foreground="#e0e0e0" FontSize="11" Margin="6,0,0,0" VerticalAlignment="Center" Width="32"/>
|
|
</DockPanel>
|
|
|
|
<!-- Fade After (seconds) -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Fade After" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
|
|
<Slider x:Name="ChatFadeSlider" Width="120" Minimum="0" Maximum="60" TickFrequency="5" IsSnapToTickEnabled="True"
|
|
Value="{Binding SelectedElement.ChatFadeSeconds, Mode=TwoWay}"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock DockPanel.Dock="Right" Text="{Binding SelectedElement.ChatFadeSeconds, StringFormat='{}{0}s'}" Foreground="#e0e0e0" FontSize="11" Margin="6,0,0,0" VerticalAlignment="Center" Width="28"/>
|
|
</DockPanel>
|
|
|
|
<!-- Toggles -->
|
|
<CheckBox Content="Show timestamps" Style="{StaticResource YtCheckBox}" Margin="0,4,0,2"
|
|
IsChecked="{Binding SelectedElement.ChatShowTimestamps, Mode=TwoWay}"/>
|
|
<CheckBox Content="Show badges" Style="{StaticResource YtCheckBox}" Margin="0,0,0,2"
|
|
IsChecked="{Binding SelectedElement.ChatShowBadges, Mode=TwoWay}"/>
|
|
</StackPanel>
|
|
|
|
<!-- Stream Alerts config -->
|
|
<StackPanel Visibility="{Binding SelectedElement.IsAlertBox, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
|
<TextBlock Text="Stream Alerts" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,6"/>
|
|
<TextBlock Text="The built-in clip plays on every alert (Super Chat, membership…) unless a custom video is picked."
|
|
Foreground="#888" FontSize="10" TextWrapping="Wrap" Margin="0,0,0,6"/>
|
|
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Built-in video" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="84"/>
|
|
<ToggleButton Style="{StaticResource PillToggle}"
|
|
IsChecked="{Binding SelectedElement.AlertUseDefaultVideo, Mode=TwoWay}"
|
|
HorizontalAlignment="Left"/>
|
|
</DockPanel>
|
|
|
|
<Grid Margin="0,0,0,4"
|
|
Visibility="{Binding SelectedElement.AlertUseDefaultVideo, Converter={StaticResource InverseBoolToVis}}">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
<TextBox Grid.Column="0" Text="{Binding SelectedElement.AlertVideoPath, Mode=OneWay}"
|
|
Style="{StaticResource YtTextBoxCompact}"
|
|
IsReadOnly="True" Foreground="#a0a0b0" VerticalAlignment="Center"/>
|
|
<Button Grid.Column="1" Content="Browse…" Command="{Binding BrowseAlertVideoCommand}"
|
|
CommandParameter="{Binding SelectedElement}" Style="{StaticResource YtButtonSecondary}"
|
|
Margin="4,0,0,0" Padding="8,2"/>
|
|
</Grid>
|
|
|
|
<Button Content="Reset to built-in" Command="{Binding ResetAlertVideoCommand}"
|
|
CommandParameter="{Binding SelectedElement}" Style="{StaticResource YtButtonSecondary}"
|
|
HorizontalAlignment="Left" Padding="8,2" Margin="0,0,4,0"/>
|
|
<TextBlock Text="Custom clips are referenced from disk — never stored in the layout."
|
|
Foreground="#888" FontSize="10" TextWrapping="Wrap" Margin="0,4,0,6"/>
|
|
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Message ticker" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="84"/>
|
|
<ToggleButton Style="{StaticResource PillToggle}"
|
|
IsChecked="{Binding SelectedElement.AlertShowTicker, Mode=TwoWay}"
|
|
HorizontalAlignment="Left"/>
|
|
</DockPanel>
|
|
|
|
<!-- Announcement display: scroll (marquee), flash (pulse) or
|
|
solid (still). Inline items, not a bound ItemsSource —
|
|
same shape as the chat Font selector above. -->
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Display" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="84"/>
|
|
<ComboBox SelectedValue="{Binding SelectedElement.AlertDisplayMethod, 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>
|
|
|
|
<DockPanel Margin="0,0,0,4">
|
|
<TextBlock DockPanel.Dock="Left" Text="Volume" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="84"/>
|
|
<Slider Width="120" Minimum="0" Maximum="1"
|
|
Value="{Binding SelectedElement.AlertVideoVolume, Mode=TwoWay}"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock DockPanel.Dock="Right" Text="{Binding SelectedElement.AlertVideoVolume, StringFormat={}{0:0%}}" Foreground="#e0e0e0" FontSize="11" Margin="6,0,0,0" VerticalAlignment="Center" Width="32"/>
|
|
</DockPanel>
|
|
</StackPanel>
|
|
|
|
<!-- Background properties -->
|
|
<StackPanel Margin="0,8,0,0"
|
|
Visibility="{Binding SelectedElement.IsBackground, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Background" Style="{StaticResource SubHeader}"/>
|
|
<StackPanel Margin="4,0,0,0">
|
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
|
|
<TextBlock Text="Use default background:" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center"/>
|
|
<ToggleButton Style="{StaticResource PillToggle}"
|
|
IsChecked="{Binding SelectedElement.UseDefaultBackground, Mode=TwoWay}"
|
|
Margin="8,0,0,0"/>
|
|
</StackPanel>
|
|
<Grid Margin="0,0,0,4"
|
|
Visibility="{Binding SelectedElement.UseDefaultBackground, Converter={StaticResource InverseBoolToVis}}">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
<TextBox Grid.Column="0" Text="{Binding SelectedElement.CustomBackgroundPath, Mode=OneWay}"
|
|
Style="{StaticResource YtTextBoxCompact}"
|
|
IsReadOnly="True" Foreground="#a0a0b0" VerticalAlignment="Center"/>
|
|
<Button Grid.Column="1" Content="Browse…" Command="{Binding BrowseBackgroundCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" Margin="4,0,0,0" Padding="8,2"/>
|
|
</Grid>
|
|
<TextBlock Text="{Binding BackgroundSizeMismatchText}"
|
|
Foreground="#ef4444" FontSize="11" TextWrapping="Wrap"
|
|
Margin="0,4,0,0"
|
|
Visibility="{Binding BackgroundSizeMismatch, Converter={StaticResource BoolToVis}}"/>
|
|
</StackPanel>
|
|
</StackPanel>
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
</Grid>
|
|
|
|
<!-- Live mode: YouTube Live Chat (entire left panel replaced) -->
|
|
<Grid Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBoolToVis}}">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<TextBlock Grid.Row="0" Text="LIVE CHAT" Style="{StaticResource SectionHeader}"/>
|
|
|
|
<!-- Inactive overlay (shown when NOT live) -->
|
|
<StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"
|
|
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Chat is inactive" Foreground="#a0a0b0" FontSize="13"
|
|
HorizontalAlignment="Center" Margin="0,0,0,4"/>
|
|
<TextBlock Text="Chat becomes active once LlamaCasty is connected and streaming on YouTube."
|
|
Foreground="#666" FontSize="11" TextWrapping="Wrap"
|
|
HorizontalAlignment="Center" TextAlignment="Center" MaxWidth="240"/>
|
|
</StackPanel>
|
|
|
|
<!-- Pinned message -->
|
|
<Border Grid.Row="1" VerticalAlignment="Top" Margin="4,4,4,0" Padding="6,4" CornerRadius="4"
|
|
Background="#1a2a3a" BorderBrush="#38bdf8" BorderThickness="1"
|
|
Visibility="{Binding PinnedChatMessage, Converter={StaticResource NotNullToVis}}">
|
|
<StackPanel>
|
|
<TextBlock Text="{Binding PinnedChatMessage.AuthorName}" Foreground="#38bdf8" FontWeight="SemiBold" FontSize="11"/>
|
|
<TextBlock Text="{Binding PinnedChatMessage.Message}" Foreground="#e0e0e0" FontSize="11" TextWrapping="Wrap"/>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- Message feed (shown when live) -->
|
|
<ListBox Grid.Row="1" Background="Transparent" BorderThickness="0" Margin="0,40,0,0"
|
|
ItemsSource="{Binding ChatMessages}"
|
|
ItemContainerStyle="{StaticResource YtListBoxItem}"
|
|
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource InverseBoolToVis}}"
|
|
x:Name="ChatListBox">
|
|
<ListBox.ItemTemplate><DataTemplate>
|
|
<Border x:Name="MessageBorder" Padding="6,4" CornerRadius="4">
|
|
<Border.Style>
|
|
<Style TargetType="Border">
|
|
<Setter Property="Background" Value="Transparent"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsSuperChat}" Value="True">
|
|
<Setter Property="Background" Value="#2a1a0a"/>
|
|
<Setter Property="BorderBrush" Value="#eab308"/>
|
|
<Setter Property="BorderThickness" Value="1,0,1,0"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
<StackPanel>
|
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,2">
|
|
<TextBlock Text="⭐" FontSize="10" Margin="0,0,4,0"
|
|
VerticalAlignment="Center"
|
|
Visibility="{Binding IsMember, Converter={StaticResource BoolToVis}}"/>
|
|
<TextBlock Text="{Binding AuthorName}" Foreground="#e94560"
|
|
FontWeight="Bold" FontSize="12" VerticalAlignment="Center"/>
|
|
<TextBlock Text="{Binding SuperChatAmount, StringFormat=${0:F2}}"
|
|
Foreground="#eab308" FontWeight="Bold" FontSize="12"
|
|
Margin="8,0,0,0" VerticalAlignment="Center"
|
|
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
|
|
<TextBlock Text="{Binding SuperChatCurrency}" Foreground="#eab308"
|
|
FontSize="11" Margin="2,0,0,0" VerticalAlignment="Center"
|
|
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
|
|
</StackPanel>
|
|
<TextBlock Text="{Binding Message}" Foreground="#e0e0e0"
|
|
TextWrapping="Wrap" Margin="0,2,0,0"/>
|
|
</StackPanel>
|
|
</Border>
|
|
</DataTemplate></ListBox.ItemTemplate>
|
|
</ListBox>
|
|
</Grid>
|
|
</Grid>
|
|
</Border>
|
|
</UserControl>
|