717 lines
50 KiB
XML
717 lines
50 KiB
XML
<Window x:Class="ytLive.MainWindow"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
xmlns:vm="clr-namespace:ytLive.ViewModels"
|
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"
|
|
mc:Ignorable="d"
|
|
Title="{Binding WindowTitle}" Height="720" Width="1280"
|
|
MinWidth="1180" MinHeight="600"
|
|
Icon="/ytLive;component/Assets/llama-logo-icon.png"
|
|
Background="#1a1a2e"
|
|
WindowStartupLocation="CenterScreen"
|
|
Closing="MainWindow_Closing"
|
|
Loaded="MainWindow_Loaded"
|
|
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown">
|
|
|
|
<Window.DataContext>
|
|
<vm:MainViewModel/>
|
|
</Window.DataContext>
|
|
|
|
<Window.TaskbarItemInfo>
|
|
<TaskbarItemInfo x:Name="TaskbarInfo"/>
|
|
</Window.TaskbarItemInfo>
|
|
|
|
<Window.Resources>
|
|
<ResourceDictionary>
|
|
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
|
<Helpers:InverseBoolToVisibilityConverter x:Key="InverseBoolToVis"
|
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
|
<Helpers:NotNullToVisibilityConverter x:Key="NotNullToVis"
|
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
|
|
|
<!-- Red dot shown in the taskbar icon while live -->
|
|
<DrawingImage x:Key="LiveOverlay">
|
|
<DrawingImage.Drawing>
|
|
<DrawingGroup>
|
|
<GeometryDrawing Brush="#e94560">
|
|
<GeometryDrawing.Geometry>
|
|
<EllipseGeometry Center="8,8" RadiusX="8" RadiusY="8"/>
|
|
</GeometryDrawing.Geometry>
|
|
</GeometryDrawing>
|
|
</DrawingGroup>
|
|
</DrawingImage.Drawing>
|
|
</DrawingImage>
|
|
|
|
<!-- Eye icon: open when visible, slashed when hidden -->
|
|
<Style x:Key="EyeIconStyle" TargetType="Path">
|
|
<Setter Property="Data" Value="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsHidden}" Value="True">
|
|
<Setter Property="Data" Value="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
|
|
<Style x:Key="EyeButton" TargetType="Button" BasedOn="{StaticResource IconButton}">
|
|
<Setter Property="ToolTip" Value="Hide Scene"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsHidden}" Value="True">
|
|
<Setter Property="ToolTip" Value="Show Scene"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</ResourceDictionary>
|
|
</Window.Resources>
|
|
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
<RowDefinition Height="Auto"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- ═══ TOP BAR: Stream Controls ═══ -->
|
|
<Border Grid.Row="0" Background="{Binding TopBarBackground}" Padding="16,10">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- Logo / Brand -->
|
|
<TextBlock Grid.Column="0" Text="ytLlive"
|
|
Foreground="#e94560" FontSize="22" FontWeight="Bold"
|
|
VerticalAlignment="Center" Margin="0,0,24,0"/>
|
|
|
|
<!-- LIVE badge (center) -->
|
|
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="{Binding LiveIndicatorVisible, Converter={StaticResource BoolToVis}}">
|
|
<Ellipse Width="12" Height="12" Fill="White" VerticalAlignment="Center"
|
|
Opacity="{Binding LivePulseOpacity}"/>
|
|
<TextBlock Text="LIVE" Foreground="White" FontSize="16" FontWeight="Bold"
|
|
VerticalAlignment="Center" Margin="8,0,0,0"/>
|
|
<TextBlock Text="{Binding LiveElapsedText}" Foreground="White" FontSize="14"
|
|
FontFamily="Consolas" VerticalAlignment="Center" Margin="14,0,0,0"/>
|
|
</StackPanel>
|
|
|
|
<!-- Single three-state action button -->
|
|
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
|
<Button Content="Start Stream" Style="{StaticResource YtButton}"
|
|
Command="{Binding StartStreamCommand}"
|
|
Visibility="{Binding ShowStartStream, Converter={StaticResource BoolToVis}}"/>
|
|
<Button Content="End Stream" Style="{StaticResource YtButton}"
|
|
Background="#333" Command="{Binding EndStreamCommand}"
|
|
Visibility="{Binding IsLive, Converter={StaticResource BoolToVis}}"/>
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ═══ MIDDLE: Main Content ═══ -->
|
|
<Grid Grid.Row="1" Margin="8">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="220"/>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="300"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- LEFT PANEL: Scenes & Sources -->
|
|
<Border Grid.Column="0" Background="#16213e" CornerRadius="6" Padding="12">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="180"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Scenes -->
|
|
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
|
<TextBlock Text="SCENES" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
|
<Button ToolTip="Add Scene" Command="{Binding AddSceneCommand}"
|
|
Style="{StaticResource IconButton}" Margin="4,-4,0,0">
|
|
<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="SceneList" Grid.Row="1" Background="Transparent" BorderThickness="0"
|
|
ItemsSource="{Binding Scenes}"
|
|
SelectedItem="{Binding ActiveScene}"
|
|
SelectionChanged="SceneList_SelectionChanged"
|
|
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 IsHidden}" Value="True">
|
|
<Setter Property="Opacity" Value="0.45"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Helpers:FocusPreservingListBox.ItemContainerStyle>
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<Grid Margin="0,2">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<TextBlock Grid.Column="0" 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="0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
|
|
Background="#0f3460" Foreground="#e0e0e0" BorderThickness="0"
|
|
Padding="4,2" VerticalContentAlignment="Center"
|
|
IsVisibleChanged="SceneNameBox_IsVisibleChanged"
|
|
KeyDown="SceneNameBox_KeyDown" LostFocus="SceneNameBox_LostFocus">
|
|
<TextBox.Style>
|
|
<Style TargetType="TextBox">
|
|
<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="1" ToolTip="Edit Scene Name"
|
|
Command="{Binding DataContext.EditSceneCommand, 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}"
|
|
Command="{Binding DataContext.ToggleSceneVisibilityCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
|
CommandParameter="{Binding}">
|
|
<Path Style="{StaticResource EyeIconStyle}" Fill="#d0d0d0"
|
|
Width="13" Height="13" Stretch="Uniform"/>
|
|
</Button>
|
|
|
|
<Button Grid.Column="3" ToolTip="Delete Scene"
|
|
Command="{Binding DataContext.RemoveSceneCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
|
CommandParameter="{Binding}" Style="{StaticResource IconButton}">
|
|
<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>
|
|
|
|
<!-- Sources (for active scene) -->
|
|
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,12,0,0">
|
|
<TextBlock Text="SOURCES" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
|
<Button ToolTip="Add Source" 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 AddSourceCommand}" CommandParameter="webcam"
|
|
IsEnabled="{Binding CanAddWebcam}"
|
|
ToolTip="One webcam at a time — it's already in your stream"/>
|
|
<MenuItem Header="Screen" Command="{Binding AddSourceCommand}" CommandParameter="screen"/>
|
|
<MenuItem Header="Background" Command="{Binding AddSourceCommand}" CommandParameter="background"/>
|
|
<MenuItem Header="Image" Command="{Binding AddImageCommand}"/>
|
|
<MenuItem Header="Text" Command="{Binding AddSourceCommand}" CommandParameter="text"/>
|
|
</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="3" Background="Transparent" BorderThickness="0"
|
|
ItemsSource="{Binding ActiveScene.Sources}"
|
|
SelectedItem="{Binding SelectedSource, 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>
|
|
</Helpers:FocusPreservingListBox.ItemContainerStyle>
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<Grid Margin="0,2">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"
|
|
Padding="4,2" TextTrimming="CharacterEllipsis"/>
|
|
|
|
<Button Grid.Column="1" ToolTip="Remove Source"
|
|
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
|
CommandParameter="{Binding}" Style="{StaticResource IconButton}">
|
|
<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="3" Text="No sources yet" Foreground="#555" FontSize="12"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- CENTER: Preview Area -->
|
|
<Border Grid.Column="1" Background="#0a0a1a" CornerRadius="6" Margin="8,0"
|
|
BorderBrush="{Binding PreviewGlowBrush}" BorderThickness="{Binding PreviewGlowThickness}">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<TextBlock Grid.Row="0" Text="{Binding ActiveScene.Name}"
|
|
Foreground="#e0e0e0" FontSize="15" FontWeight="SemiBold"
|
|
Margin="14,12,14,0"/>
|
|
|
|
<Grid x:Name="PreviewGrid" Grid.Row="1"
|
|
PreviewMouseLeftButtonDown="Preview_MouseLeftButtonDown"
|
|
PreviewMouseMove="Preview_MouseMove"
|
|
PreviewMouseLeftButtonUp="Preview_MouseLeftButtonUp">
|
|
<Viewbox Stretch="Uniform">
|
|
<Grid x:Name="CanvasGrid" Width="1920" Height="1080" ClipToBounds="True">
|
|
<Image Source="{Binding ActiveBackgroundImage}" Stretch="UniformToFill"
|
|
IsHitTestVisible="False"/>
|
|
<Rectangle Stroke="#22c55e" StrokeThickness="6" IsHitTestVisible="False"/>
|
|
<Canvas x:Name="OverlayCanvas" Width="1920" Height="1080">
|
|
<ItemsControl ItemsSource="{Binding ActiveScene.Sources}"
|
|
Width="1920" Height="1080">
|
|
<ItemsControl.ItemsPanel>
|
|
<ItemsPanelTemplate>
|
|
<Canvas/>
|
|
</ItemsPanelTemplate>
|
|
</ItemsControl.ItemsPanel>
|
|
<ItemsControl.ItemContainerStyle>
|
|
<Style TargetType="ContentPresenter">
|
|
<Setter Property="Canvas.Left" Value="{Binding X}"/>
|
|
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
|
|
</Style>
|
|
</ItemsControl.ItemContainerStyle>
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate>
|
|
<Grid Width="{Binding Width}" Height="{Binding Height}"
|
|
Opacity="{Binding Opacity}"
|
|
Background="Transparent"
|
|
RenderTransformOrigin="0.5,0.5">
|
|
<Grid.RenderTransform>
|
|
<ScaleTransform ScaleX="{Binding MirrorScale}"/>
|
|
</Grid.RenderTransform>
|
|
<Image Stretch="UniformToFill"
|
|
Source="{Binding DisplaySource}"
|
|
RenderOptions.BitmapScalingMode="HighQuality">
|
|
<Image.Style>
|
|
<Style TargetType="Image">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding Type}" Value="Image">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding Type}" Value="Webcam"/>
|
|
<Condition Binding="{Binding ClipShape}" Value="Traditional"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Image.Style>
|
|
</Image>
|
|
<Viewbox Stretch="Uniform">
|
|
<Grid Width="1" Height="1">
|
|
<Ellipse>
|
|
<Ellipse.Style>
|
|
<Style TargetType="Ellipse">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding Type}" Value="Webcam"/>
|
|
<Condition Binding="{Binding ClipShape}" Value="Round"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Ellipse.Style>
|
|
<Ellipse.Fill>
|
|
<ImageBrush ImageSource="{Binding DisplaySource}" Stretch="UniformToFill"/>
|
|
</Ellipse.Fill>
|
|
</Ellipse>
|
|
</Grid>
|
|
</Viewbox>
|
|
</Grid>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
|
|
<Grid x:Name="SelectionOverlay" IsHitTestVisible="False"
|
|
Canvas.Left="{Binding SelectedSource.X}" Canvas.Top="{Binding SelectedSource.Y}"
|
|
Width="{Binding SelectedSource.Width}" Height="{Binding SelectedSource.Height}">
|
|
<Rectangle Stroke="#e94560" StrokeDashArray="4 3" StrokeThickness="3"/>
|
|
<Ellipse Width="26" Height="26" Fill="#e94560" Stroke="White" StrokeThickness="2"
|
|
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-13,-13"/>
|
|
</Grid>
|
|
|
|
<ItemsControl ItemsSource="{Binding DimRects}" IsHitTestVisible="False">
|
|
<ItemsControl.ItemsPanel>
|
|
<ItemsPanelTemplate>
|
|
<Canvas/>
|
|
</ItemsPanelTemplate>
|
|
</ItemsControl.ItemsPanel>
|
|
<ItemsControl.ItemContainerStyle>
|
|
<Style TargetType="ContentPresenter">
|
|
<Setter Property="Canvas.Left" Value="{Binding X}"/>
|
|
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
|
|
</Style>
|
|
</ItemsControl.ItemContainerStyle>
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate>
|
|
<Rectangle Width="{Binding Width}" Height="{Binding Height}"
|
|
Fill="#000000" Opacity="0.55"/>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
|
|
<Rectangle IsHitTestVisible="False" Stroke="#e94560" StrokeThickness="3"
|
|
Canvas.Left="{Binding OutputRectX}" Canvas.Top="{Binding OutputRectY}"
|
|
Width="{Binding OutputRectWidth}" Height="{Binding OutputRectHeight}"
|
|
Visibility="{Binding IsOutputCropped, Converter={StaticResource BoolToVis}}"/>
|
|
|
|
<Grid x:Name="BrandFlashLayer" Width="1920" Height="1080" IsHitTestVisible="False" Opacity="0">
|
|
<Grid.Style>
|
|
<Style TargetType="Grid">
|
|
<Setter Property="Opacity" Value="0"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding BrandFlashActive}" Value="True">
|
|
<DataTrigger.EnterActions>
|
|
<BeginStoryboard>
|
|
<Storyboard>
|
|
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
|
From="0" To="0.25" Duration="0:0:0.25"/>
|
|
</Storyboard>
|
|
</BeginStoryboard>
|
|
</DataTrigger.EnterActions>
|
|
<DataTrigger.ExitActions>
|
|
<BeginStoryboard>
|
|
<Storyboard>
|
|
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
|
From="0.25" To="0" Duration="0:0:0.25"/>
|
|
</Storyboard>
|
|
</BeginStoryboard>
|
|
</DataTrigger.ExitActions>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Grid.Style>
|
|
<Viewbox Stretch="Uniform" MaxWidth="1600" MaxHeight="800"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
|
<TextBlock Text="made with ytLlive!" FontWeight="Bold" Foreground="White"
|
|
FontSize="180" TextAlignment="Center"/>
|
|
</Viewbox>
|
|
</Grid>
|
|
</Canvas>
|
|
</Grid>
|
|
</Viewbox>
|
|
<Border HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,8,8,0"
|
|
Background="#CC16213e" CornerRadius="4" Padding="8,3" IsHitTestVisible="False">
|
|
<TextBlock Text="{Binding ResolutionBadgeText}" Foreground="#e0e0e0" FontSize="12"/>
|
|
</Border>
|
|
<TextBlock Text="Preview" Foreground="#333" FontSize="24"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="{Binding ShowPreviewPlaceholder, Converter={StaticResource BoolToVis}}"/>
|
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="{Binding ShowEmptySceneHint, Converter={StaticResource BoolToVis}}">
|
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
|
<TextBlock Text="🖥️" Foreground="White" FontSize="30" Margin="0,0,20,0"/>
|
|
<TextBlock Text="📷" Foreground="White" FontSize="30" Margin="0,0,20,0"/>
|
|
<TextBlock Text="🖼️" Foreground="White" FontSize="30" Margin="0,0,20,0"/>
|
|
<TextBlock Text="✏️" Foreground="White" FontSize="30"/>
|
|
</StackPanel>
|
|
<TextBlock Text="Make it yours!" Foreground="#e0e0e0" FontSize="18"
|
|
FontWeight="SemiBold" HorizontalAlignment="Center" Margin="0,18,0,0"/>
|
|
<TextBlock Text="Add sources — your webcam, your screen, images, and text — to craft a brand and identity that's unmistakably you."
|
|
Foreground="#a0a0b0" FontSize="13" Margin="0,8,0,0"
|
|
TextWrapping="Wrap" MaxWidth="440" TextAlignment="Center"
|
|
HorizontalAlignment="Center"/>
|
|
</StackPanel>
|
|
|
|
<Border x:Name="OpacityChip" HorizontalAlignment="Left" VerticalAlignment="Bottom"
|
|
Background="#D9000000" CornerRadius="6" Padding="10,8" Margin="12,0,0,12"
|
|
Visibility="Collapsed">
|
|
<StackPanel>
|
|
<StackPanel Orientation="Horizontal">
|
|
<TextBlock Text="Opacity" Foreground="#e0e0e0" FontSize="13" VerticalAlignment="Center"/>
|
|
<Slider x:Name="OpacitySlider" Width="120" Minimum="0" Maximum="1" Margin="10,0,0,0"
|
|
Value="{Binding SelectedSource.Opacity, Mode=TwoWay}"
|
|
ValueChanged="OpacitySlider_ValueChanged" VerticalAlignment="Center"/>
|
|
<TextBlock x:Name="OpacityValueText" Text="100%" Foreground="#e0e0e0" FontSize="13"
|
|
Width="40" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
<StackPanel Orientation="Horizontal" Margin="0,10,0,0"
|
|
Visibility="{Binding IsWebcamSelected, Converter={StaticResource BoolToVis}}">
|
|
<Button Content="{Binding SelectedSource.MirrorButtonText}"
|
|
Style="{StaticResource YtButtonSecondary}" Padding="10,4"
|
|
Click="MirrorButton_Click"/>
|
|
<Button Content="{Binding SelectedSource.ShapeButtonText}"
|
|
Style="{StaticResource YtButtonSecondary}" Padding="10,4"
|
|
Margin="8,0,0,0" Click="ShapeButton_Click"/>
|
|
</StackPanel>
|
|
<TextBlock Text="Drag to move · drag the corner dot to resize"
|
|
Foreground="#a0a0b0" FontSize="11" Margin="0,4,0,0"/>
|
|
</StackPanel>
|
|
</Border>
|
|
</Grid>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- RIGHT PANEL: Chat -->
|
|
<Border Grid.Column="2" Background="#16213e" CornerRadius="6" Padding="12">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<TextBlock Grid.Row="0" Text="LIVE CHAT" Style="{StaticResource SectionHeader}"/>
|
|
|
|
<StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Chat is inactive" Foreground="#e0e0e0" FontSize="18"
|
|
FontWeight="SemiBold" HorizontalAlignment="Center"/>
|
|
<TextBlock Text="Chat becomes active once ytLlive is connected and streaming on YouTube."
|
|
Foreground="#a0a0b0" FontSize="13" Margin="0,8,0,0"
|
|
TextWrapping="Wrap" MaxWidth="260" TextAlignment="Center"
|
|
HorizontalAlignment="Center"/>
|
|
</StackPanel>
|
|
|
|
<ListBox Grid.Row="1" Background="Transparent" BorderThickness="0"
|
|
ItemsSource="{Binding ChatMessages}"
|
|
ItemContainerStyle="{StaticResource YtListBoxItem}"
|
|
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource InverseBoolToVis}}"
|
|
Foreground="#e0e0e0" FontSize="12">
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<StackPanel Margin="0,2">
|
|
<TextBlock>
|
|
<Run Text="{Binding AuthorName, Mode=OneWay}" FontWeight="Bold" Foreground="#e94560"/>
|
|
<Run Text=": "/>
|
|
<Run Text="{Binding Message, Mode=OneWay}" Foreground="#e0e0e0"/>
|
|
</TextBlock>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</ListBox>
|
|
</Grid>
|
|
</Border>
|
|
</Grid>
|
|
|
|
<!-- ═══ BOTTOM BAR: Stream Health ═══ -->
|
|
<Border Grid.Row="2" Background="#0f3460" Padding="16,6">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<StackPanel Orientation="Horizontal">
|
|
<TextBlock Text="Bitrate:" Style="{StaticResource YtLabel}" Margin="0,0,4,0"/>
|
|
<TextBlock Style="{StaticResource YtLabel}"
|
|
Foreground="White" Margin="0,0,20,0">
|
|
<Run Text="{Binding CurrentHealth.CurrentBitrate, Mode=OneWay}"/>
|
|
<Run Text=" Mbps"/>
|
|
</TextBlock>
|
|
|
|
<TextBlock Text="FPS:" Style="{StaticResource YtLabel}" Margin="0,0,4,0"/>
|
|
<TextBlock Style="{StaticResource YtLabel}"
|
|
Foreground="White" Margin="0,0,20,0">
|
|
<Run Text="{Binding CurrentHealth.FPS, Mode=OneWay, StringFormat={}{0:0}}"/>
|
|
</TextBlock>
|
|
|
|
<TextBlock Text="Dropped:" Style="{StaticResource YtLabel}" Margin="0,0,4,0"/>
|
|
<TextBlock Text="{Binding CurrentHealth.DroppedFrames}" Style="{StaticResource YtLabel}"
|
|
Foreground="White" Margin="0,0,20,0"/>
|
|
|
|
<TextBlock Text="Duration:" Style="{StaticResource YtLabel}" Margin="0,0,4,0"/>
|
|
<TextBlock Text="{Binding CurrentHealth.StreamDuration}" Style="{StaticResource YtLabel}"
|
|
Foreground="White"/>
|
|
|
|
<TextBlock Text="{Binding CurrentHealth.HealthMessage}" Style="{StaticResource YtLabel}"
|
|
Foreground="#e94560" Margin="20,0,0,0"/>
|
|
</StackPanel>
|
|
|
|
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
|
<ComboBox Style="{StaticResource YtComboBox}" MinWidth="150"
|
|
ItemsSource="{Binding QualityOptions}"
|
|
SelectedItem="{Binding SelectedQuality}"
|
|
DisplayMemberPath="Label"
|
|
ToolTip="{Binding ResolutionHelp}"
|
|
IsEnabled="{Binding IsOffline}"/>
|
|
<Button ToolTip="Menu" Style="{StaticResource IconButton}" Click="GearButton_Click">
|
|
<Button.ContextMenu>
|
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
|
<MenuItem Header="Save Layout" Command="{Binding SaveLayoutCommand}"/>
|
|
<MenuItem Header="Save Layout As…" Command="{Binding SaveLayoutAsCommand}"/>
|
|
<MenuItem Header="Open Layout…" Command="{Binding OpenLayoutCommand}"/>
|
|
<Separator/>
|
|
<MenuItem Header="App Settings" Command="{Binding OpenSettingsCommand}"/>
|
|
<MenuItem Header="Report Bug" Command="{Binding OpenBugCommand}"/>
|
|
<MenuItem Header="Feature Request" Command="{Binding OpenFeatureCommand}"/>
|
|
<MenuItem Header="About" Command="{Binding OpenAboutCommand}"/>
|
|
</ContextMenu>
|
|
</Button.ContextMenu>
|
|
<Path Data="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"
|
|
Fill="#d0d0d0" Width="16" Height="16" Stretch="Uniform"/>
|
|
</Button>
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ═══ OVERLAY: Settings / Bug / Feature / About ═══ -->
|
|
<Grid Grid.RowSpan="3"
|
|
Visibility="{Binding IsAnyOverlayOpen, Converter={StaticResource BoolToVis}}">
|
|
<Button Background="#99000000" BorderThickness="0" Cursor="Hand"
|
|
Command="{Binding CloseOverlayCommand}">
|
|
<Button.Template>
|
|
<ControlTemplate TargetType="Button">
|
|
<Border Background="{TemplateBinding Background}"/>
|
|
</ControlTemplate>
|
|
</Button.Template>
|
|
</Button>
|
|
|
|
<Border Background="#1a1a2e" CornerRadius="8" Width="520" MaxHeight="560"
|
|
VerticalAlignment="Center" HorizontalAlignment="Center"
|
|
Padding="24" BorderBrush="#333" BorderThickness="1">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Header -->
|
|
<Grid Grid.Row="0">
|
|
<TextBlock Text="{Binding OverlayTitle}" FontSize="18" FontWeight="Bold"
|
|
Foreground="#e0e0e0" VerticalAlignment="Center"/>
|
|
<Button HorizontalAlignment="Right" ToolTip="Close" Style="{StaticResource IconButton}"
|
|
Command="{Binding CloseOverlayCommand}">
|
|
<Path Data="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
|
|
Fill="#d0d0d0" Width="14" Height="14" Stretch="Uniform"/>
|
|
</Button>
|
|
</Grid>
|
|
|
|
<!-- App Settings -->
|
|
<StackPanel Grid.Row="1" Margin="0,16,0,0"
|
|
Visibility="{Binding IsSettingsOpen, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Default Stream Title" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
|
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
|
|
Text="{Binding DefaultStreamTitle, UpdateSourceTrigger=PropertyChanged}"/>
|
|
|
|
<TextBlock Text="Default Stream Description" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
|
<TextBox Style="{StaticResource YtTextBox}" Height="70" AcceptsReturn="True" TextWrapping="Wrap"
|
|
Margin="0,0,0,12"
|
|
Text="{Binding DefaultStreamDescription, UpdateSourceTrigger=PropertyChanged}"
|
|
VerticalScrollBarVisibility="Auto"/>
|
|
|
|
<TextBlock Text="Default Visibility" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
|
<ComboBox Style="{StaticResource YtComboBox}" HorizontalAlignment="Left" MinWidth="140"
|
|
ItemsSource="{Binding Visibilities}"
|
|
SelectedItem="{Binding DefaultStreamVisibility}"/>
|
|
|
|
<TextBlock Text="These prefill the Start Stream dialog. Scene and stream settings are stored on the stream itself."
|
|
Style="{StaticResource YtLabel}" Margin="0,16,0,0" TextWrapping="Wrap"/>
|
|
</StackPanel>
|
|
|
|
<!-- Report Bug -->
|
|
<StackPanel Grid.Row="1" Margin="0,16,0,0"
|
|
Visibility="{Binding IsBugOpen, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="What went wrong?" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
|
<TextBox Style="{StaticResource YtTextBox}" Height="110" AcceptsReturn="True" TextWrapping="Wrap"
|
|
Margin="0,0,0,12"
|
|
Text="{Binding BugReportText, UpdateSourceTrigger=PropertyChanged}"
|
|
VerticalScrollBarVisibility="Auto"/>
|
|
|
|
<TextBlock Text="Email (optional)" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
|
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
|
|
Text="{Binding BugReportEmail, UpdateSourceTrigger=PropertyChanged}"/>
|
|
|
|
<TextBlock Text="Opens a pre-filled email to the ytLlive team." Style="{StaticResource YtLabel}"/>
|
|
<Button Content="Send Report" Command="{Binding SubmitBugCommand}"
|
|
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
|
|
</StackPanel>
|
|
|
|
<!-- Feature Request -->
|
|
<StackPanel Grid.Row="1" Margin="0,16,0,0"
|
|
Visibility="{Binding IsFeatureOpen, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="What would you like to see?" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
|
<TextBox Style="{StaticResource YtTextBox}" Height="110" AcceptsReturn="True" TextWrapping="Wrap"
|
|
Margin="0,0,0,12"
|
|
Text="{Binding FeatureRequestText, UpdateSourceTrigger=PropertyChanged}"
|
|
VerticalScrollBarVisibility="Auto"/>
|
|
|
|
<TextBlock Text="Email (optional)" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
|
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
|
|
Text="{Binding FeatureRequestEmail, UpdateSourceTrigger=PropertyChanged}"/>
|
|
|
|
<TextBlock Text="Opens a pre-filled email to the ytLlive team." Style="{StaticResource YtLabel}"/>
|
|
<Button Content="Send Request" Command="{Binding SubmitFeatureCommand}"
|
|
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
|
|
</StackPanel>
|
|
|
|
<!-- About -->
|
|
<StackPanel Grid.Row="1" Margin="0,16,0,0"
|
|
Visibility="{Binding IsAboutOpen, Converter={StaticResource BoolToVis}}">
|
|
<Image Source="/ytLive;component/Assets/llama-logo.png" Height="140" Stretch="Uniform"
|
|
HorizontalAlignment="Center" Margin="0,0,0,12"/>
|
|
<TextBlock Text="ytLlive" FontSize="22" FontWeight="Bold" Foreground="#e94560"
|
|
HorizontalAlignment="Center"/>
|
|
<TextBlock Text="{Binding AppVersionLabel}" Foreground="#a0a0b0" FontSize="12"
|
|
HorizontalAlignment="Center" Margin="0,4,0,0"/>
|
|
<TextBlock Text="Live streaming for YouTube — simple enough that even the most right-brained person can intuit it."
|
|
Foreground="#a0a0b0" FontSize="13" TextWrapping="Wrap" MaxWidth="380"
|
|
TextAlignment="Center" Margin="0,16,0,0"/>
|
|
<TextBlock Text="Free forever — live streams carry a brief "made with ytLlive!" flash. A one-time unlock removes it and adds alerts."
|
|
Foreground="#888" FontSize="12" TextWrapping="Wrap" MaxWidth="380"
|
|
TextAlignment="Center" Margin="0,12,0,0"/>
|
|
<TextBlock Text="Made by gramps · llama chile shop" Foreground="#a0a0b0" FontSize="12"
|
|
HorizontalAlignment="Center" Margin="0,16,0,8"/>
|
|
<Button Content="Visit llama chile shop on YouTube" Command="{Binding OpenChannelCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" HorizontalAlignment="Center" Margin="0,0,0,4"/>
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
</Grid>
|
|
</Grid>
|
|
</Window>
|