Scene/source builder with image overlays, reuse dialog, and SQLite layout persistence
This commit is contained in:
+511
-34
@@ -6,8 +6,12 @@
|
||||
xmlns:vm="clr-namespace:ytLive.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
Title="{Binding WindowTitle}" Height="720" Width="1280"
|
||||
MinWidth="1180" MinHeight="600"
|
||||
Icon="/Assets/llama-logo-icon.png"
|
||||
Background="#1a1a2e"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Closing="MainWindow_Closing"
|
||||
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown">
|
||||
|
||||
<Window.DataContext>
|
||||
<vm:MainViewModel/>
|
||||
@@ -19,6 +23,33 @@
|
||||
|
||||
<Window.Resources>
|
||||
<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"/>
|
||||
|
||||
<!-- YouTube logo shown on the Connect button -->
|
||||
<DrawingImage x:Key="YoutubeLogo">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF0000">
|
||||
<GeometryDrawing.Geometry>
|
||||
<RectangleGeometry Rect="0,0,66,47" RadiusX="10" RadiusY="10"/>
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="White">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry>
|
||||
<PathFigure IsClosed="True" StartPoint="25,14">
|
||||
<LineSegment Point="25,33"/>
|
||||
<LineSegment Point="45,23.5"/>
|
||||
</PathFigure>
|
||||
</PathGeometry>
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<!-- Red dot shown in the taskbar icon while live -->
|
||||
<DrawingImage x:Key="LiveOverlay">
|
||||
@@ -59,6 +90,48 @@
|
||||
<Setter Property="Background" Value="#16213e"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="IconButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="4"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="3"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#26ffffff"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<Style x:Key="YtTextBox" TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#0f3460"/>
|
||||
<Setter Property="Foreground" Value="#e0e0e0"/>
|
||||
@@ -68,6 +141,15 @@
|
||||
<Setter Property="CaretBrush" Value="White"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="YtComboBox" TargetType="ComboBox">
|
||||
<Setter Property="Background" Value="#0f3460"/>
|
||||
<Setter Property="Foreground" Value="#e0e0e0"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="8,6"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="YtLabel" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="#a0a0b0"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
@@ -98,7 +180,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Logo / Brand -->
|
||||
<TextBlock Grid.Column="0" Text="ytLive"
|
||||
<TextBlock Grid.Column="0" Text="ytLlive"
|
||||
Foreground="#e94560" FontSize="22" FontWeight="Bold"
|
||||
VerticalAlignment="Center" Margin="0,0,24,0"/>
|
||||
|
||||
@@ -114,11 +196,19 @@
|
||||
FontFamily="Consolas" VerticalAlignment="Center" Margin="14,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Single two-state action button -->
|
||||
<!-- Single three-state action button -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Style="{StaticResource YtButtonSecondary}" Command="{Binding ConnectCommand}"
|
||||
Visibility="{Binding IsNotConnected, Converter={StaticResource BoolToVis}}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{StaticResource YoutubeLogo}" Width="22" Height="16"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock Text="Connect" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Content="Start Stream" Style="{StaticResource YtButton}"
|
||||
Command="{Binding StartStreamCommand}"
|
||||
Visibility="{Binding IsOffline, Converter={StaticResource BoolToVis}}"/>
|
||||
Visibility="{Binding ShowStartStream, Converter={StaticResource BoolToVis}}"/>
|
||||
<Button Content="End Stream" Style="{StaticResource YtButton}"
|
||||
Background="#333" Command="{Binding EndStreamCommand}"
|
||||
Visibility="{Binding IsLive, Converter={StaticResource BoolToVis}}"/>
|
||||
@@ -141,32 +231,159 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="180"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Scenes -->
|
||||
<TextBlock Grid.Row="0" Text="SCENES" Style="{StaticResource SectionHeader}"/>
|
||||
<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>
|
||||
|
||||
<ListBox Grid.Row="1" Background="Transparent" BorderThickness="0"
|
||||
<ListBox 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">
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHidden}" Value="True">
|
||||
<Setter Property="Opacity" Value="0.45"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" Padding="4,4"/>
|
||||
<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>
|
||||
</ListBox>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,8,0,0">
|
||||
<Button Content="+" Style="{StaticResource YtButtonSecondary}"
|
||||
Command="{Binding AddSceneCommand}" Padding="8,4" Margin="0,0,4,0"/>
|
||||
<Button Content="−" Style="{StaticResource YtButtonSecondary}"
|
||||
Command="{Binding RemoveSceneCommand}" Padding="8,4"/>
|
||||
<!-- 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"/>
|
||||
<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>
|
||||
|
||||
<!-- Sources (for active scene) -->
|
||||
<TextBlock Grid.Row="3" Text="SOURCES" Style="{StaticResource SectionHeader}" Margin="0,12,0,0"/>
|
||||
<ListBox 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">
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
</ListBox.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>
|
||||
</ListBox>
|
||||
|
||||
<TextBlock Grid.Row="3" Text="No sources yet" Foreground="#555" FontSize="12"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -174,9 +391,105 @@
|
||||
<Border Grid.Column="1" Background="#0a0a1a" CornerRadius="6" Margin="8,0"
|
||||
BorderBrush="{Binding PreviewGlowBrush}" BorderThickness="{Binding PreviewGlowThickness}">
|
||||
<Grid>
|
||||
<TextBlock Text="Preview" Foreground="#333" FontSize="24"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<!-- TODO: D3DImage or MediaElement for video preview -->
|
||||
<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>
|
||||
<Image Width="{Binding Width}" Height="{Binding Height}"
|
||||
Opacity="{Binding Opacity}" Stretch="UniformToFill"
|
||||
Source="{Binding ImageSource}"
|
||||
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>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Image.Style>
|
||||
</Image>
|
||||
</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>
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
@@ -190,8 +503,19 @@
|
||||
|
||||
<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}"
|
||||
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource InverseBoolToVis}}"
|
||||
Foreground="#e0e0e0" FontSize="12">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -211,26 +535,179 @@
|
||||
|
||||
<!-- ═══ BOTTOM BAR: Stream Health ═══ -->
|
||||
<Border Grid.Row="2" Background="#0f3460" Padding="16,6">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Bitrate:" Style="{StaticResource YtLabel}" Margin="0,0,4,0"/>
|
||||
<TextBlock Text="{Binding CurrentHealth.CurrentBitrate}" Style="{StaticResource YtLabel}"
|
||||
Foreground="White" Margin="0,0,20,0"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="FPS:" Style="{StaticResource YtLabel}" Margin="0,0,4,0"/>
|
||||
<TextBlock Text="{Binding CurrentHealth.FPS}" Style="{StaticResource YtLabel}"
|
||||
Foreground="White" Margin="0,0,20,0"/>
|
||||
<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="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="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="Duration:" Style="{StaticResource YtLabel}" Margin="0,0,4,0"/>
|
||||
<TextBlock Text="{Binding CurrentHealth.StreamDuration}" Style="{StaticResource YtLabel}"
|
||||
Foreground="White"/>
|
||||
<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="{Binding CurrentHealth.HealthMessage}" Style="{StaticResource YtLabel}"
|
||||
Foreground="#e94560" Margin="20,0,0,0"/>
|
||||
</StackPanel>
|
||||
<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>
|
||||
|
||||
<Button Grid.Column="1" 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>
|
||||
</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"/>
|
||||
|
||||
<TextBlock Text="STREAM SETTINGS" Style="{StaticResource SectionHeader}" Margin="0,20,0,8"/>
|
||||
|
||||
<TextBlock Text="Stream Quality" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
|
||||
<ComboBox Style="{StaticResource YtComboBox}" HorizontalAlignment="Left" MinWidth="140"
|
||||
ItemsSource="{Binding StreamQualities}"
|
||||
SelectedItem="{Binding StreamQuality}"/>
|
||||
|
||||
<TextBlock Text="Switching quality updates the stream metrics in the status bar. 1080p60 is the standard."
|
||||
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="/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 with a watermark. A one-time unlock removes the watermark 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>
|
||||
|
||||
Reference in New Issue
Block a user