2f135687cd
Same root cause as IsChatBox — SourceType binding fails on WebcamSceneConfig, WPF defaults Visibility to Visible without calling the converter. Added virtual bool IsCountdown on SceneElement, override in Source. Swapped binding to BoolToVis. Removed dead SourceTypeToVisibility converters.
1517 lines
120 KiB
XML
1517 lines
120 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:models="clr-namespace:ytLive.Models"
|
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"
|
|
mc:Ignorable="d"
|
|
Title="{Binding WindowTitle}" Height="720" Width="1280"
|
|
MinWidth="1180" MinHeight="600"
|
|
Icon="/ytLive;component/Assets/llamacasty-icon.png"
|
|
Background="#1a1a2e"
|
|
WindowStartupLocation="CenterScreen"
|
|
Closing="MainWindow_Closing"
|
|
Loaded="MainWindow_Loaded"
|
|
Activated="MainWindow_Activated"
|
|
Deactivated="MainWindow_Deactivated"
|
|
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"/>
|
|
<Helpers:NullToVisibilityConverter x:Key="NullToVis"
|
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
|
<Helpers:HiddenBoolToVisibilityConverter x:Key="HiddenBoolToVis"
|
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
|
<Helpers:EnumToBoolConverter x:Key="EnumToBool"
|
|
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 (source rows) -->
|
|
<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 IsVisible}" Value="False">
|
|
<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 Layer"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsVisible}" Value="False">
|
|
<Setter Property="ToolTip" Value="Show Layer"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</ResourceDictionary>
|
|
</Window.Resources>
|
|
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<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 — clicking opens the in-app About overlay -->
|
|
<Button Grid.Column="0" Background="Transparent" BorderThickness="0" Cursor="Hand"
|
|
VerticalAlignment="Center" Margin="0,0,24,0" ToolTip="About ytLlive"
|
|
Command="{Binding OpenAboutCommand}">
|
|
<Button.Template>
|
|
<ControlTemplate TargetType="Button">
|
|
<ContentPresenter/>
|
|
</ControlTemplate>
|
|
</Button.Template>
|
|
<TextBlock Text="ytLlive" Foreground="#e94560" FontSize="22" FontWeight="Bold"/>
|
|
</Button>
|
|
|
|
<!-- REC sign (center): dark offline, red live, darker red when private -->
|
|
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
|
<Ellipse Width="12" Height="12" VerticalAlignment="Center"
|
|
Fill="{Binding RecDotBrush}" Opacity="{Binding RecDotOpacity}"/>
|
|
<TextBlock Text="REC" FontSize="16" FontWeight="Bold"
|
|
Foreground="{Binding RecTextBrush}" VerticalAlignment="Center" Margin="8,0,0,0"/>
|
|
<Border Background="#8f1f1f" CornerRadius="3" Padding="6,1" Margin="8,0,0,0"
|
|
VerticalAlignment="Center"
|
|
Visibility="{Binding IsLivePrivate, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="PRIVATE" FontSize="11" FontWeight="Bold" Foreground="White"/>
|
|
</Border>
|
|
<TextBlock Text="{Binding LiveElapsedText}" Foreground="White" FontSize="14"
|
|
FontFamily="Consolas" VerticalAlignment="Center" Margin="14,0,0,0"
|
|
Visibility="{Binding IsLive, Converter={StaticResource BoolToVis}}"/>
|
|
</StackPanel>
|
|
|
|
<!-- Three-state top bar: logged-out / idle / live -->
|
|
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
|
<!-- Status light -->
|
|
<Ellipse Width="8" Height="8" Margin="0,0,8,0" VerticalAlignment="Center">
|
|
<Ellipse.Style>
|
|
<Style TargetType="Ellipse">
|
|
<Setter Property="Fill" Value="#ef4444"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsConnected}" Value="True">
|
|
<Setter Property="Fill" Value="#22c55e"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsLive}" Value="True">
|
|
<Setter Property="Fill" Value="#e94560"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Ellipse.Style>
|
|
</Ellipse>
|
|
<!-- Avatar (connected only) -->
|
|
<Border Width="26" Height="26" CornerRadius="13" Background="#16213e" ClipToBounds="True"
|
|
Margin="0,0,8,0" VerticalAlignment="Center"
|
|
ToolTip="{Binding AccountDisplayName}"
|
|
Visibility="{Binding IsConnected, Converter={StaticResource BoolToVis}}">
|
|
<Image x:Name="AvatarImage" Stretch="UniformToFill"/>
|
|
</Border>
|
|
<!-- Log In button (disconnected) -->
|
|
<Button Content="Log In" Style="{StaticResource YtButton}"
|
|
Command="{Binding StartStreamCommand}"
|
|
Visibility="{Binding IsConnected, Converter={StaticResource InverseBoolToVis}}"/>
|
|
<!-- Start Stream button (connected + offline) -->
|
|
<Button Content="Start Stream" Style="{StaticResource YtButton}"
|
|
Command="{Binding StartStreamCommand}"
|
|
Visibility="{Binding ShowStartStream, Converter={StaticResource BoolToVis}}"/>
|
|
<!-- End Stream button (live) -->
|
|
<Button Content="End Stream" Style="{StaticResource YtButton}"
|
|
Background="#333" Command="{Binding EndStreamCommand}"
|
|
Visibility="{Binding IsLive, Converter={StaticResource BoolToVis}}"/>
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ═══ MIDDLE: Main Content ═══ -->
|
|
<!-- YouTube health banner (TASK 5 item 3): report-by-exception — visible
|
|
ONLY while a warning/error configuration issue exists. Amber for
|
|
warning, darker red for error (HealthIssueBackground). -->
|
|
<Border Grid.Row="1" Background="{Binding HealthIssueBackground}"
|
|
Visibility="{Binding HealthIssueBanner, Converter={StaticResource NotNullToVis}}">
|
|
<TextBlock Text="{Binding HealthIssueBanner}" Foreground="White" FontSize="13"
|
|
FontWeight="SemiBold" Margin="16,8" TextTrimming="CharacterEllipsis"/>
|
|
</Border>
|
|
|
|
<Grid Grid.Row="2" Margin="8">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="1*" MinWidth="220" MaxWidth="360"/>
|
|
<ColumnDefinition Width="2*"/>
|
|
<ColumnDefinition Width="1*" MinWidth="300" MaxWidth="400"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- LEFT PANEL: Scenes & Sources -->
|
|
<Border Grid.Column="0" Background="#16213e" CornerRadius="6" Padding="12">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Scenes: the app has five canonical scenes. Work with less,
|
|
never more — the "+" only offers the missing ones, and is
|
|
hidden entirely once all five are back. -->
|
|
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
|
<TextBlock Text="SCREENS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
|
<Button ToolTip="Add Missing Screen" Click="AddSceneButton_Click"
|
|
Style="{StaticResource IconButton}" Margin="4,-4,0,0"
|
|
Visibility="{Binding ShowAddScene, Converter={StaticResource BoolToVis}}">
|
|
<Button.ContextMenu>
|
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"
|
|
ItemsSource="{Binding MissingScenes}">
|
|
<ContextMenu.ItemContainerStyle>
|
|
<Style TargetType="MenuItem">
|
|
<Setter Property="Header" Value="{Binding}"/>
|
|
<Setter Property="Command"
|
|
Value="{Binding DataContext.AddSceneCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
|
<Setter Property="CommandParameter" Value="{Binding}"/>
|
|
</Style>
|
|
</ContextMenu.ItemContainerStyle>
|
|
</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="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">
|
|
<TextBlock Text="{Binding Name, Converter={x:Static models:SceneCatalog.DisplayNameConverter}}" VerticalAlignment="Center"
|
|
Padding="4,2" TextTrimming="CharacterEllipsis"/>
|
|
</Grid>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</Helpers:FocusPreservingListBox>
|
|
|
|
<!-- Visual separation between the two listboxes: a hairline
|
|
with breathing room on either side (the creator's fix). -->
|
|
<Rectangle Grid.Row="2" Height="1" Fill="#2c3a5c" Margin="0,10,0,2"/>
|
|
|
|
<!-- Sources (for active scene) -->
|
|
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="0,8,0,0">
|
|
<TextBlock Text="LAYERS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
|
<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 CanAddWebcamToActiveScene}"
|
|
ToolTip="One webcam at a time — it's already in your stream"/>
|
|
<MenuItem Header="Screen" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.DisplayCapture}"/>
|
|
<MenuItem Header="Background" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.Background}"/>
|
|
<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" 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}"
|
|
ToolTip="YouTube live chat overlay on stream"/>
|
|
</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="4" Background="Transparent" BorderThickness="0"
|
|
ItemsSource="{Binding ActiveScene.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>
|
|
</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>
|
|
|
|
<Grid.ContextMenu>
|
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
|
<MenuItem Header="Change Capture…" Click="BackdropMenu_ChangeCapture"
|
|
Visibility="{Binding IsBackdrop, Converter={StaticResource BoolToVis}}"/>
|
|
<MenuItem Header="Refresh Capture" Click="BackdropMenu_RefreshCapture"
|
|
Visibility="{Binding IsBackdrop, Converter={StaticResource BoolToVis}}"/>
|
|
<Separator Visibility="{Binding IsBackdrop, Converter={StaticResource BoolToVis}}"/>
|
|
<MenuItem Header="Remove Layer" Click="SourceMenu_Remove"
|
|
Visibility="{Binding IsBackdrop, Converter={StaticResource InverseBoolToVis}}"/>
|
|
</ContextMenu>
|
|
</Grid.ContextMenu>
|
|
|
|
<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="SourceNameBox_IsVisibleChanged"
|
|
KeyDown="SourceNameBox_KeyDown" LostFocus="SourceNameBox_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 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="2" 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="3" ToolTip="Remove Layer"
|
|
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
|
CommandParameter="{Binding}" Style="{StaticResource IconButton}"
|
|
Visibility="{Binding IsBackdrop, 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="4" Text="No layers yet" Foreground="#555" FontSize="12"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
|
|
|
<!-- ELEMENTS panel: per-element config beneath Sources.
|
|
Collapses entirely when no element is selected. -->
|
|
<Rectangle Grid.Row="5" Height="1" Fill="#2c3a5c" Margin="0,10,0,2"
|
|
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}"/>
|
|
<StackPanel Grid.Row="6" Orientation="Horizontal" Margin="0,8,0,0"
|
|
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
|
<TextBlock Text="PROPERTIES" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="7" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
|
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
|
<StackPanel x:Name="ElementsPanel" Margin="0,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>
|
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,4"
|
|
Visibility="{Binding IsWebcamSelected, Converter={StaticResource BoolToVis}}">
|
|
<Button Content="{Binding SelectedElement.MirrorButtonText}"
|
|
Style="{StaticResource YtButtonSecondary}" Padding="10,4"
|
|
Click="MirrorButton_Click"/>
|
|
<Button Content="{Binding SelectedElement.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="10" Margin="0,2,0,0"/>
|
|
</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" Margin="0,0,0,2"/>
|
|
<DockPanel Margin="0,0,0,6">
|
|
<Button DockPanel.Dock="Right" Content="..." Width="24" Height="24"
|
|
Click="WebcamBorderColorPicker_Click" Margin="4,0,0,0"
|
|
ToolTip="Pick color"/>
|
|
<TextBox Text="{Binding SelectedElement.BorderColor, UpdateSourceTrigger=PropertyChanged}"
|
|
Background="#0f3460" Foreground="#e0d0e0" BorderThickness="1"
|
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="12"
|
|
MaxLength="7" ToolTip="#RRGGBB"/>
|
|
</DockPanel>
|
|
<TextBlock Text="Border Thickness" 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="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>
|
|
</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="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"
|
|
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"
|
|
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}"
|
|
Background="#0f3460" Foreground="#e0d0e0" BorderThickness="1"
|
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="12"
|
|
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"
|
|
Background="#0f3460" Foreground="#e0e0e0" FontSize="11"
|
|
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}"
|
|
Background="#0f3460" Foreground="#e0e0e0" BorderThickness="1"
|
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="11" 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}"
|
|
Background="#0f3460" Foreground="#e0e0e0" BorderThickness="1"
|
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="11" 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}"
|
|
Background="#0f3460" Foreground="#e0e0e0" BorderThickness="1"
|
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="11" 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}"
|
|
Background="#0f3460" Foreground="#e0e0e0" BorderThickness="1"
|
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="11" 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" Foreground="#a0a0b0" FontSize="11" Margin="0,4,0,2"
|
|
IsChecked="{Binding SelectedElement.ChatShowTimestamps, Mode=TwoWay}"/>
|
|
<CheckBox Content="Show badges" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"
|
|
IsChecked="{Binding SelectedElement.ChatShowBadges, Mode=TwoWay}"/>
|
|
</StackPanel>
|
|
|
|
<!-- Placeholder hint when no config available -->
|
|
<TextBlock Text="No configurable properties" Foreground="#555" FontSize="11"
|
|
HorizontalAlignment="Center" Margin="0,8,0,0"
|
|
Visibility="{Binding IsElementsPanelEmpty, Converter={StaticResource BoolToVis}}"/>
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
</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" Background="Transparent">
|
|
<Grid.ContextMenu>
|
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
|
<MenuItem Header="Show Webcam" Command="{Binding ShowWebcamCommand}"/>
|
|
<MenuItem Header="Show Desktop" IsCheckable="True"
|
|
IsChecked="{Binding BackdropShowDesktop}"
|
|
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"
|
|
ToolTip="Show desktop when no fullscreen game is detected"/>
|
|
<MenuItem Header="Capture Display"
|
|
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"
|
|
ItemsSource="{Binding Displays}">
|
|
<MenuItem.ItemContainerStyle>
|
|
<Style TargetType="MenuItem">
|
|
<Setter Property="Header" Value="{Binding Label}"/>
|
|
<Setter Property="Command"
|
|
Value="{Binding DataContext.SetBackdropDisplayCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
|
<Setter Property="CommandParameter" Value="{Binding}"/>
|
|
</Style>
|
|
</MenuItem.ItemContainerStyle>
|
|
</MenuItem>
|
|
<MenuItem Header="Change Capture…" Command="{Binding ChangeCaptureCommand}"
|
|
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"
|
|
ToolTip="Pick any window with the OS picker"/>
|
|
<MenuItem Header="Refresh Capture" Command="{Binding RefreshCaptureCommand}"
|
|
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"/>
|
|
</ContextMenu>
|
|
</Grid.ContextMenu>
|
|
<!-- Live desktop/game backdrop (bottom layer; the static
|
|
background renders above it). -->
|
|
<Image Source="{Binding BackdropImage}" Stretch="UniformToFill"
|
|
Visibility="{Binding BackdropVisible, Converter={StaticResource BoolToVis}}"
|
|
IsHitTestVisible="False"/>
|
|
<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.Elements}"
|
|
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.Style>
|
|
<Style TargetType="Grid">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsVisible}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
<!-- The backdrop is a fixed full-frame layer (rendered separately,
|
|
below the static background): never hit-testable, so it can't be
|
|
selected, dragged, or bring up the webcam context menu. -->
|
|
<DataTrigger Binding="{Binding IsBackdrop}" Value="True">
|
|
<Setter Property="IsHitTestVisible" Value="False"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Grid.Style>
|
|
<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 IsImageSource}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsChatBox}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding IsWebcam}" Value="True"/>
|
|
<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">
|
|
<!-- Round webcam: a plain Image (the same efficient WriteableBitmap
|
|
path as Traditional) clipped to a unit circle and scaled by the
|
|
Viewbox — deliberately NOT an ImageBrush, whose per-frame
|
|
re-rasterization at full scale stalls the preview. -->
|
|
<Image Stretch="UniformToFill"
|
|
Source="{Binding DisplaySource}"
|
|
RenderOptions.BitmapScalingMode="HighQuality">
|
|
<Image.Style>
|
|
<Style TargetType="Image">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding IsWebcam}" Value="True"/>
|
|
<Condition Binding="{Binding ClipShape}" Value="Round"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Image.Style>
|
|
<Image.Clip>
|
|
<EllipseGeometry Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5"/>
|
|
</Image.Clip>
|
|
</Image>
|
|
</Grid>
|
|
</Viewbox>
|
|
|
|
<!-- OBS-style border: stroked rect (Traditional) / centered circle (Round).
|
|
Animations are driven by code-behind in WebcamMenu_SetBorderAnimation
|
|
because DataTrigger storyboards in DataTemplates cannot be stopped
|
|
(no ExitActions namescope support). -->
|
|
<Rectangle IsHitTestVisible="False"
|
|
Stroke="{Binding BorderBrush}"
|
|
StrokeThickness="{Binding BorderWidth}">
|
|
<Rectangle.Style>
|
|
<Style TargetType="Rectangle">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding HasBorder}" Value="True"/>
|
|
<Condition Binding="{Binding ClipShape}" Value="Traditional"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Rectangle.Style>
|
|
</Rectangle>
|
|
<Ellipse IsHitTestVisible="False"
|
|
Stroke="{Binding BorderBrush}"
|
|
StrokeThickness="{Binding BorderWidth}"
|
|
Width="{Binding RoundBorderSize}"
|
|
Height="{Binding RoundBorderSize}"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
|
<Ellipse.Style>
|
|
<Style TargetType="Ellipse">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding HasBorder}" Value="True"/>
|
|
<Condition Binding="{Binding ClipShape}" Value="Round"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Ellipse.Style>
|
|
</Ellipse>
|
|
|
|
<Grid.ContextMenu>
|
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
|
<MenuItem Header="Change Webcam…" Click="WebcamMenu_ChangeWebcam"
|
|
Visibility="{Binding IsWebcam, Converter={StaticResource BoolToVis}}"/>
|
|
<Separator/>
|
|
<MenuItem Header="Border Thickness">
|
|
<StackPanel Orientation="Horizontal" Margin="8,2">
|
|
<TextBlock Text="Thickness" Foreground="#e0e0e0" VerticalAlignment="Center"/>
|
|
<Slider Width="120" Minimum="0" Maximum="20" Margin="8,0,0,0"
|
|
Value="{Binding BorderWidth, Mode=TwoWay}"
|
|
VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
</MenuItem>
|
|
<MenuItem Header="Border Opacity">
|
|
<StackPanel Orientation="Horizontal" Margin="8,2">
|
|
<TextBlock Text="Opacity" Foreground="#e0e0e0" VerticalAlignment="Center"/>
|
|
<Slider Width="120" Minimum="0" Maximum="1" Margin="8,0,0,0"
|
|
Value="{Binding BorderOpacity, Mode=TwoWay}"
|
|
VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
</MenuItem>
|
|
<MenuItem Header="Border Color">
|
|
<MenuItem Header="None" Tag="" IsCheckable="True"
|
|
IsChecked="{Binding BorderColor, Converter={StaticResource EnumToBool}, ConverterParameter=''}"
|
|
Click="WebcamMenu_SetBorderColor"/>
|
|
<MenuItem Header="#ffffff" Tag="#ffffff" IsCheckable="True"
|
|
IsChecked="{Binding BorderColor, Converter={StaticResource EnumToBool}, ConverterParameter=#ffffff}"
|
|
Click="WebcamMenu_SetBorderColor"/>
|
|
<MenuItem Header="#000000" Tag="#000000" IsCheckable="True"
|
|
IsChecked="{Binding BorderColor, Converter={StaticResource EnumToBool}, ConverterParameter=#000000}"
|
|
Click="WebcamMenu_SetBorderColor"/>
|
|
<MenuItem Header="#e94560" Tag="#e94560" IsCheckable="True"
|
|
IsChecked="{Binding BorderColor, Converter={StaticResource EnumToBool}, ConverterParameter=#e94560}"
|
|
Click="WebcamMenu_SetBorderColor"/>
|
|
<MenuItem Header="#22c55e" Tag="#22c55e" IsCheckable="True"
|
|
IsChecked="{Binding BorderColor, Converter={StaticResource EnumToBool}, ConverterParameter=#22c55e}"
|
|
Click="WebcamMenu_SetBorderColor"/>
|
|
<MenuItem Header="#eab308" Tag="#eab308" IsCheckable="True"
|
|
IsChecked="{Binding BorderColor, Converter={StaticResource EnumToBool}, ConverterParameter=#eab308}"
|
|
Click="WebcamMenu_SetBorderColor"/>
|
|
<MenuItem Header="#38bdf8" Tag="#38bdf8" IsCheckable="True"
|
|
IsChecked="{Binding BorderColor, Converter={StaticResource EnumToBool}, ConverterParameter=#38bdf8}"
|
|
Click="WebcamMenu_SetBorderColor"/>
|
|
</MenuItem>
|
|
<MenuItem Header="Border Effect">
|
|
<MenuItem Header="None" Tag="None" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=None}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Pulse" Tag="Pulse" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Pulse}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Chase" Tag="Chase" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Chase}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Rainbow" Tag="Rainbow" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Rainbow}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Shimmer" Tag="Shimmer" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Shimmer}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Marching Ants" Tag="MarchingAnts" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=MarchingAnts}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Glow" Tag="Glow" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Glow}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Electricity" Tag="Electricity" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Electricity}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
<MenuItem Header="Sparkles" Tag="Sparkles" IsCheckable="True"
|
|
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Sparkles}"
|
|
Click="WebcamMenu_SetBorderAnimation"/>
|
|
</MenuItem>
|
|
<Separator/>
|
|
<MenuItem Header="Hide in this scene" Click="WebcamMenu_HideInScene"/>
|
|
<MenuItem Header="Remove" Click="WebcamMenu_Remove"/>
|
|
</ContextMenu>
|
|
</Grid.ContextMenu>
|
|
</Grid>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
|
|
<Grid x:Name="SelectionOverlay" IsHitTestVisible="False"
|
|
Canvas.Left="{Binding SelectedElement.X}" Canvas.Top="{Binding SelectedElement.Y}"
|
|
Width="{Binding SelectedElement.Width}" Height="{Binding SelectedElement.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>
|
|
|
|
<!-- Social bar: one centered horizontal row at the top or bottom of
|
|
the frame, green glow while enabled. Global resource — never a
|
|
Source, no Sources entry. Click toggles top ⇄ bottom. -->
|
|
<Grid x:Name="SocialBarElement" Width="1920" Canvas.Top="{Binding SocialBarTop}"
|
|
Background="Transparent"
|
|
Visibility="{Binding SocialBarVisible, Converter={StaticResource BoolToVis}}"
|
|
MouseLeftButtonDown="SocialBar_MouseLeftButtonDown"
|
|
Cursor="Hand">
|
|
<Grid.Effect>
|
|
<DropShadowEffect Color="{Binding SocialBarGlowBrush.Color}"
|
|
BlurRadius="18" ShadowDepth="0" Opacity="0.9"/>
|
|
</Grid.Effect>
|
|
<ItemsControl ItemsSource="{Binding Socials.Entries}"
|
|
HorizontalAlignment="Center" Margin="40,8"
|
|
IsHitTestVisible="False">
|
|
<ItemsControl.ItemsPanel>
|
|
<ItemsPanelTemplate>
|
|
<StackPanel Orientation="Horizontal"/>
|
|
</ItemsPanelTemplate>
|
|
</ItemsControl.ItemsPanel>
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate>
|
|
<StackPanel Orientation="Horizontal" Margin="0,0,20,0">
|
|
<Path Data="{Binding LogoData}" Fill="White" Width="24" Height="24"
|
|
Stretch="Uniform" VerticalAlignment="Center"/>
|
|
<TextBlock Text="{Binding Handle}" Foreground="White"
|
|
FontSize="14" VerticalAlignment="Center"
|
|
Margin="8,0,0,0"/>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
</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>
|
|
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Margin="8,8,0,0"
|
|
Background="#CCe94560" CornerRadius="4" Padding="8,3" IsHitTestVisible="False"
|
|
Visibility="{Binding WebcamError, Converter={StaticResource NotNullToVis}}">
|
|
<TextBlock Text="{Binding WebcamError}" Foreground="White" FontSize="12"
|
|
MaxWidth="520" TextTrimming="CharacterEllipsis"/>
|
|
</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 layers — 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>
|
|
|
|
<!-- Desktop/game audio bar: overlaid at the bottom of the
|
|
preview, always visible. Meter + mute + volume control
|
|
live here; desktop audio itself is automatic WASAPI
|
|
loopback. Volume + mute are stream-honest AND scale the
|
|
TRAX music in the headphones, so the creator hears the
|
|
control work. -->
|
|
<Border HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
|
Background="#D9000000" CornerRadius="6" Padding="12,8"
|
|
Margin="0,0,0,12">
|
|
<StackPanel Orientation="Horizontal">
|
|
<!-- TRAX sits left of the "Desktop Audio" label
|
|
(the creator's pick): it rides the desktop
|
|
channel, so the music control lives with the
|
|
desktop audio controls. -->
|
|
<Button Style="{StaticResource YtButtonSecondary}" Padding="8,2"
|
|
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0"
|
|
ToolTipService.ToolTip="{Binding TraxToolTip}"
|
|
ToolTipService.Placement="Top"
|
|
Click="TraxButton_Click"
|
|
PreviewMouseRightButtonUp="TraxButton_PreviewMouseRightButtonUp">
|
|
<StackPanel Orientation="Horizontal">
|
|
<Ellipse Width="8" Height="8" Fill="{Binding TraxDotBrush}"
|
|
Margin="0,0,6,0" VerticalAlignment="Center"/>
|
|
<TextBlock Text="TRAX"/>
|
|
</StackPanel>
|
|
</Button>
|
|
<TextBlock Text="Desktop Audio" Foreground="#a0a0b0" FontSize="11"
|
|
FontWeight="SemiBold" VerticalAlignment="Center" Margin="0,0,8,0"
|
|
ToolTip="Desktop/game audio via WASAPI loopback (automatic); TRAX music rides the same channel"/>
|
|
<Border Width="288" Height="14" CornerRadius="7" Background="#3a3b52" Margin="0,0,8,0"
|
|
ClipToBounds="True" VerticalAlignment="Center">
|
|
<Grid>
|
|
<Rectangle Width="57" Fill="#4a4520" HorizontalAlignment="Left" Margin="173,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
<Rectangle Width="58" Fill="#4a2222" HorizontalAlignment="Left" Margin="230,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
<Border HorizontalAlignment="Left" Width="{Binding GameMeterFillWidth}"
|
|
Background="{Binding GameMeterBrush}" Opacity="0.75"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="36,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="72,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="108,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="144,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="180,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="216,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="252,0,0,0"/>
|
|
<Rectangle Width="2" Fill="#00000080" HorizontalAlignment="Left" Margin="173,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
<Rectangle Width="2" Fill="#00000080" HorizontalAlignment="Left" Margin="230,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled"
|
|
Visibility="{Binding TraxError, Converter={StaticResource NotNullToVis}}"
|
|
VerticalAlignment="Center" Margin="4,0">
|
|
<TextBlock Text="{Binding TraxError}" Foreground="#e94560" FontSize="9"
|
|
FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
</ScrollViewer>
|
|
</Grid>
|
|
</Border>
|
|
<Grid Width="16" Height="16" Cursor="Hand" Margin="0,0,8,0"
|
|
VerticalAlignment="Center" ToolTip="{Binding GameMuteText}"
|
|
MouseLeftButtonUp="GameSpeaker_MouseLeftButtonUp">
|
|
<Path Fill="#d0d0d0" Stretch="Uniform"
|
|
Data="M3,9v6h4l5,5V4L7,9H3zM16.5,12c0,-1.77 -1,-3.29 -2.5,-4.03v8.05c1.5,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"
|
|
Visibility="{Binding GameMuted, Converter={StaticResource InverseBoolToVis}}"/>
|
|
<Path Fill="#ef4444" Stretch="Uniform"
|
|
Data="M3,9v6h4l5,5V4L7,9H3zM16.5,12c0,-1.77 -1,-3.29 -2.5,-4.03v8.05c1.5,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"
|
|
Visibility="{Binding GameMuted, Converter={StaticResource BoolToVis}}"/>
|
|
<Path Stroke="#ef4444" StrokeThickness="2.5" StrokeEndLineCap="Round" Stretch="Uniform"
|
|
Data="M21,3 L3,21"
|
|
Visibility="{Binding GameMuted, Converter={StaticResource BoolToVis}}"/>
|
|
</Grid>
|
|
<Slider Width="130" Minimum="0" Maximum="1"
|
|
Value="{Binding GameAudioVolume, Mode=TwoWay}" VerticalAlignment="Center"
|
|
PreviewMouseLeftButtonDown="GameVolumeSlider_PreviewMouseLeftButtonDown"
|
|
PreviewMouseLeftButtonUp="GameVolumeSlider_PreviewMouseLeftButtonUp"
|
|
LostMouseCapture="GameVolumeSlider_LostMouseCapture"/>
|
|
</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"
|
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
|
x:Name="ChatListBox">
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<Border x:Name="MessageBorder" Padding="6,4" CornerRadius="4" Margin="0,1">
|
|
<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">
|
|
<!-- Member badge -->
|
|
<TextBlock Text="⭐" FontSize="10" Margin="0,0,3,0"
|
|
VerticalAlignment="Center">
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsMember}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
|
|
<!-- Author name -->
|
|
<TextBlock FontWeight="Bold" Foreground="#e94560"
|
|
Text="{Binding AuthorName, Mode=OneWay}"
|
|
VerticalAlignment="Center"/>
|
|
|
|
<!-- Super Chat amount -->
|
|
<TextBlock Foreground="#eab308" FontWeight="Bold"
|
|
FontSize="11" Margin="6,0,0,0"
|
|
VerticalAlignment="Center">
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsSuperChat}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
<Run Text="💰 $"/>
|
|
<Run Text="{Binding SuperChatAmount, Mode=OneWay, StringFormat=F2}"/>
|
|
<Run Text="{Binding SuperChatCurrency, Mode=OneWay}"/>
|
|
</TextBlock>
|
|
|
|
<!-- Timestamp -->
|
|
<TextBlock Foreground="#555" FontSize="10"
|
|
Margin="6,0,0,0" VerticalAlignment="Center">
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding Timestamp}" Value="{x:Null}">
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
<TextBlock.Text>
|
|
<Binding Path="Timestamp" StringFormat="{}{0:HH:mm}"/>
|
|
</TextBlock.Text>
|
|
</TextBlock>
|
|
</StackPanel>
|
|
|
|
<!-- Message -->
|
|
<TextBlock Text="{Binding Message, Mode=OneWay}"
|
|
Foreground="#e0e0e0" TextWrapping="Wrap"
|
|
Margin="0,2,0,0"/>
|
|
</StackPanel>
|
|
</Border>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</ListBox>
|
|
</Grid>
|
|
</Border>
|
|
</Grid>
|
|
|
|
<!-- ═══ BOTTOM BAR: Stream Health + Audio ═══ -->
|
|
<Border Grid.Row="3" Background="#0f3460" Padding="16,6">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Line 1: Social controls on the LEFT (under scenes/sources),
|
|
sound meter + volume control CENTERED beneath the preview panel.
|
|
Audio is KISS: desktop/game audio is automatic (WASAPI loopback);
|
|
the mic meter is the creator's only footer audio control (the
|
|
game audio bar lives INSIDE the preview window, overlaid at its
|
|
bottom edge while a full-screen game is up, and carries the
|
|
TRAX button left of its "Desktop Audio" label). -->
|
|
<Grid Grid.Row="0">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="220"/>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="300"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- Social controls: centered under the scenes/sources listboxes -->
|
|
<StackPanel Grid.Column="0" Orientation="Horizontal"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
|
<Button Command="{Binding OpenSocialDialogCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" Padding="12,4"
|
|
FontSize="11" VerticalAlignment="Center"
|
|
ToolTip="Add or manage your social handles">
|
|
<StackPanel Orientation="Horizontal">
|
|
<Ellipse Width="8" Height="8" Fill="{Binding SocialBarDotBrush}"
|
|
Margin="0,0,6,0" VerticalAlignment="Center"/>
|
|
<TextBlock Text="Socials"/>
|
|
</StackPanel>
|
|
</Button>
|
|
</StackPanel>
|
|
|
|
<!-- Mic meter + volume: centered under the preview panel -->
|
|
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
|
<Button Command="{Binding OpenMicPickerCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" Padding="12,4"
|
|
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0"
|
|
ToolTip="{Binding MicStatusToolTip}">
|
|
<StackPanel Orientation="Horizontal">
|
|
<Ellipse Width="8" Height="8" Fill="{Binding MicStatusBrush}"
|
|
Margin="0,0,6,0" VerticalAlignment="Center"/>
|
|
<TextBlock Text="MIC"/>
|
|
</StackPanel>
|
|
</Button>
|
|
<!-- Sound meter: muted track with a ruler scale, zone tints at
|
|
the yellow (60%) and red (80%) starts. READ-ONLY realtime
|
|
level display (fill = live level scaled by volume). -->
|
|
<Border Width="288" Height="14" CornerRadius="7" Background="#3a3b52" Margin="0,0,8,0"
|
|
ClipToBounds="True" VerticalAlignment="Center">
|
|
<Grid>
|
|
<Rectangle Width="57" Fill="#4a4520" HorizontalAlignment="Left" Margin="173,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
<Rectangle Width="58" Fill="#4a2222" HorizontalAlignment="Left" Margin="230,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
<!-- Mic source name, left-justified inside the bar; the
|
|
semi-transparent fill lets it (and the markings) show through. -->
|
|
<TextBlock Text="{Binding MicSourceName}" Foreground="#b8b8c8" FontSize="10"
|
|
VerticalAlignment="Center" Margin="6,0,0,0" MaxWidth="270"
|
|
TextTrimming="CharacterEllipsis"
|
|
Visibility="{Binding MicSourceName, Converter={StaticResource NotNullToVis}}"/>
|
|
<Border HorizontalAlignment="Left" Width="{Binding MeterFillWidth}"
|
|
Background="{Binding MeterBrush}" Opacity="0.75"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="36,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="72,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="108,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="144,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="180,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="216,0,0,0"/>
|
|
<Rectangle Width="1" Height="10" Fill="#26000000" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="252,0,0,0"/>
|
|
<Rectangle Width="2" Fill="#00000080" HorizontalAlignment="Left" Margin="173,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
<Rectangle Width="2" Fill="#00000080" HorizontalAlignment="Left" Margin="230,0,0,0"
|
|
VerticalAlignment="Stretch"/>
|
|
</Grid>
|
|
</Border>
|
|
<Grid Width="16" Height="16" Cursor="Hand" Margin="0,0,8,0"
|
|
VerticalAlignment="Center" ToolTip="{Binding MicMuteText}"
|
|
MouseLeftButtonUp="MicSpeaker_MouseLeftButtonUp">
|
|
<Path Fill="#d0d0d0" Stretch="Uniform"
|
|
Data="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.49 6,-3.31 6,-6.72H17.3z"
|
|
Visibility="{Binding MicMuted, Converter={StaticResource InverseBoolToVis}}"/>
|
|
<Path Fill="#ef4444" Stretch="Uniform"
|
|
Data="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.49 6,-3.31 6,-6.72H17.3z"
|
|
Visibility="{Binding MicMuted, Converter={StaticResource BoolToVis}}"/>
|
|
<Path Stroke="#ef4444" StrokeThickness="2.5" StrokeEndLineCap="Round" Stretch="Uniform"
|
|
Data="M21,3 L3,21"
|
|
Visibility="{Binding MicMuted, Converter={StaticResource BoolToVis}}"/>
|
|
</Grid>
|
|
<Slider Width="130" Minimum="0" Maximum="1"
|
|
Value="{Binding MicVolume, Mode=TwoWay}" VerticalAlignment="Center"
|
|
PreviewMouseLeftButtonDown="VolumeSlider_PreviewMouseLeftButtonDown"
|
|
PreviewMouseLeftButtonUp="VolumeSlider_PreviewMouseLeftButtonUp"
|
|
LostMouseCapture="VolumeSlider_LostMouseCapture"/>
|
|
</StackPanel>
|
|
</Grid>
|
|
|
|
<!-- Line 2: everything else — stream stats on the left, quality +
|
|
gear on the right. -->
|
|
<Grid Grid.Row="1" Margin="0,6,0,0">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
|
|
<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,24,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="2" 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>
|
|
</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="#0A0C1A" 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="These prefill the Start Stream dialog. Streams always start Private — change visibility on YouTube after going live."
|
|
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 -->
|
|
<Grid Grid.Row="1" Margin="0,16,0,0"
|
|
Visibility="{Binding IsAboutOpen, Converter={StaticResource BoolToVis}}">
|
|
|
|
<!-- Hub view -->
|
|
<StackPanel Visibility="{Binding IsLicensesOpen, Converter={StaticResource InverseBoolToVis}}">
|
|
<Image Source="/ytLive;component/Assets/llamacasty-logo.jpg" Height="100" 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"/>
|
|
|
|
<!-- Creator hub links -->
|
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,8">
|
|
<Button Content="llama chile shop on YouTube" Command="{Binding OpenChannelCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
|
|
<Button Content="Mastodon" Command="{Binding OpenMastodonCommand}"
|
|
Style="{StaticResource YtButtonSecondary}"/>
|
|
</StackPanel>
|
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
|
<Button Content="Buy me a coffee" Command="{Binding OpenCoffeeCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
|
|
<Button Content="Unlock Premium" Command="{Binding OpenPremiumCommand}"
|
|
Style="{StaticResource YtButtonSecondary}"
|
|
IsEnabled="{Binding IsPremiumAvailable}"
|
|
ToolTip="Coming soon — subscription billing is on the way (TASK 23)"/>
|
|
</StackPanel>
|
|
|
|
<Button Content="Licenses & legal" Command="{Binding OpenLicensesCommand}"
|
|
Style="{StaticResource YtButtonSecondary}" HorizontalAlignment="Center"
|
|
Margin="0,16,0,0"/>
|
|
</StackPanel>
|
|
|
|
<!-- Licensing/legal panel -->
|
|
<StackPanel Visibility="{Binding IsLicensesOpen, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Third-party notices & license texts" FontSize="15" FontWeight="Bold"
|
|
Foreground="#e0e0e0" Margin="0,0,0,8"/>
|
|
<Border Background="#10101e" CornerRadius="4" Padding="10" MaxHeight="360"
|
|
BorderBrush="#333" BorderThickness="1">
|
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
|
<TextBlock Text="{Binding LicensesText}" Foreground="#b8b8c8" FontSize="11"
|
|
FontFamily="Consolas" TextWrapping="Wrap"/>
|
|
</ScrollViewer>
|
|
</Border>
|
|
<Button Content="← Back to About" Style="{StaticResource YtButtonSecondary}"
|
|
HorizontalAlignment="Left" Margin="0,12,0,0"
|
|
Click="BackToAbout_Click"/>
|
|
</StackPanel>
|
|
</Grid>
|
|
</Grid>
|
|
</Border>
|
|
</Grid>
|
|
</Grid>
|
|
</Window>
|