Right rail, top bar, bottom bar and chat border: Socials slide-out drawer, tab icons and colors, EDIT toggle, stream readouts moved to the top-left cell, webcam border restored, chat border controls, 30fps vertical tier

- Socials: the dialog is a right-rail drawer (same pattern as Stream Info/YPP/TEST); the live-screen button is removed
- Right rail: Stream Info (was Stream Settings) and YPP show the YouTube logo; Socials and TEST get icons; tab colors and label colors per spec
- Top bar: EDIT toggle beside REC (orange when on, blue when off); going live on YouTube turns it off
- Stream readouts (bitrate, FPS, dropped n (x.y%), Live hh:mm:ss, health) and the resolution dropdown moved to the top-left cell; bottom bar emptied at the same height
- Webcam border drawing restored (it was removed with the CRT bezel change)
- YouTube chat layer: border color, thickness, and opacity controls
- Vertical 1080p30 tier added

Not related to the fps gate yet; no gating is enforced.
This commit is contained in:
2026-10-26 18:24:09 -07:00
parent a8814b42d0
commit 7ae30d4cfc
13 changed files with 341 additions and 102 deletions
+2 -40
View File
@@ -3,46 +3,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border Background="#0f3460" Padding="16,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Stream stats: centered under the preview pane -->
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center" 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>
<!-- Resolution dropdown: far right -->
<ComboBox Grid.Column="2" Style="{StaticResource YtComboBox}" MinWidth="150"
ItemsSource="{Binding QualityOptions}"
SelectedItem="{Binding SelectedQuality}"
DisplayMemberPath="Label"
ToolTip="{Binding ResolutionHelp}"
IsEnabled="{Binding IsOffline}"/>
<!-- Intentionally empty. The hidden label keeps the bar's height unchanged. -->
<TextBlock Style="{StaticResource YtLabel}" Text=" " Visibility="Hidden"/>
</Grid>
</Border>
</UserControl>
+32
View File
@@ -514,6 +514,38 @@
<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"/>
<!-- Border (same controls as the webcam's border) -->
<TextBlock Text="Border Color" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,2"/>
<DockPanel Margin="10,0,0,6">
<TextBox DockPanel.Dock="Left"
Text="{Binding SelectedElement.BorderColor, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource YtTextBoxCompact}"
MaxLength="7" ToolTip="#RRGGBB" Width="60"
VerticalAlignment="Center"/>
<ComboBox ItemsSource="{Binding SelectedElement.BorderColorOptions}"
SelectedItem="{Binding SelectedElement.BorderColorEntry, Mode=TwoWay}"
ItemTemplateSelector="{StaticResource BorderColorTemplates}"
Style="{StaticResource YtComboBox}"
ToolTip="Border colour" MinWidth="100"
Margin="8,0,0,0" VerticalAlignment="Center"/>
</DockPanel>
<TextBlock Text="Border Thickness" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,2"/>
<DockPanel Margin="10,0,0,6">
<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>
<TextBlock Text="Border Opacity" Foreground="#a0a0b0" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,2"/>
<DockPanel Margin="10,0,0,8">
<Slider DockPanel.Dock="Left" Width="120" Minimum="0" Maximum="1"
Value="{Binding SelectedElement.BorderOpacity, Mode=TwoWay}"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding SelectedElement.BorderOpacity, StringFormat={}{0:P0}}" Foreground="#a0a0b0" FontSize="11"
VerticalAlignment="Center" Margin="8,0,0,0" Width="34"/>
</DockPanel>
<!-- Font Size -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Font Size" Foreground="#a0a0b0" FontSize="11" VerticalAlignment="Center" Width="70"/>
+150 -23
View File
@@ -1,7 +1,8 @@
<UserControl x:Class="ytLive.Controls.PreviewPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Helpers="clr-namespace:ytLive.Helpers">
xmlns:Helpers="clr-namespace:ytLive.Helpers"
xmlns:local="clr-namespace:ytLive">
<Border Background="#0a0a1a" CornerRadius="6" Margin="8,0"
BorderBrush="{Binding PreviewGlowBrush}" BorderThickness="{Binding PreviewGlowThickness}">
<Grid>
@@ -181,6 +182,50 @@
</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"
@@ -267,8 +312,8 @@
<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"
<Rectangle Stroke="#ff8c3a" StrokeDashArray="4 3" StrokeThickness="5"/>
<Ellipse Width="26" Height="26" Fill="#ff8c3a" Stroke="White" StrokeThickness="2"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-13,-13"/>
</Grid>
@@ -516,6 +561,63 @@
</StackPanel>
</Grid>
</Border>
<!-- The Socials drawer (same pattern as the other pull-outs) -->
<Border Background="#16213e" ClipToBounds="True">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Width" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SocialsDrawerOpen}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="320" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid Width="320" Margin="14,10,14,12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,0,0,8">
<TextBlock Text="SOCIALS" Style="{StaticResource SectionHeader}" HorizontalAlignment="Left"/>
<Button HorizontalAlignment="Right" VerticalAlignment="Top"
Background="Transparent" BorderThickness="0" Cursor="Hand"
Command="{Binding OpenSocialDialogCommand}"
ToolTip="Close">
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter/>
</ControlTemplate>
</Button.Template>
<TextBlock Text="✕" Foreground="#a0a0b0" FontSize="14" FontWeight="Bold"/>
</Button>
</Grid>
<local:SocialsDialog Grid.Row="1" DataContext="{Binding SocialsDrawer}"/>
</Grid>
</Border>
<!-- The YPP-progress drawer (below Stream Settings on the rail) -->
<Border Background="#16213e" ClipToBounds="True">
<Border.Style>
@@ -750,47 +852,86 @@
</Border>
<!-- The stacked white tabs -->
<StackPanel Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Stretch">
<Border Width="30" VerticalAlignment="Center" Background="#ffffff"
<Border Width="30" VerticalAlignment="Center" Background="#ff5451"
CornerRadius="4,0,0,4" Padding="0,10" Cursor="Hand" Margin="0,4,0,0"
ToolTip="Broadcast text fields">
<Border.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding BroadcastForm.ToggleDrawerCommand}"/>
</Border.InputBindings>
<TextBlock Text="Stream Settings" Foreground="#e94560" FontWeight="Bold" FontSize="15"
<StackPanel HorizontalAlignment="Center">
<Border Width="18" Height="13" CornerRadius="3" Background="White" HorizontalAlignment="Center" Margin="0,0,0,6">
<Border.LayoutTransform><RotateTransform Angle="90"/></Border.LayoutTransform>
<Path Data="M0,0 L6,3.5 L0,7 Z" Fill="#ff0000" Width="6" Height="7"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Stream Info" Foreground="White" FontWeight="Bold" FontSize="15"
HorizontalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</StackPanel>
</Border>
<Border Width="30" VerticalAlignment="Center" Background="#ffffff"
<Border Width="30" VerticalAlignment="Center" Background="#ff5451"
CornerRadius="4,0,0,4" Padding="0,10" Cursor="Hand" Margin="0,4,0,0"
ToolTip="YouTube Partner Program progress">
<Border.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding Ypp.ToggleDrawerCommand}"/>
</Border.InputBindings>
<TextBlock Text="YPP" Foreground="#e94560" FontWeight="Bold" FontSize="15"
<StackPanel HorizontalAlignment="Center">
<Border Width="18" Height="13" CornerRadius="3" Background="White" HorizontalAlignment="Center" Margin="0,0,0,6">
<Border.LayoutTransform><RotateTransform Angle="90"/></Border.LayoutTransform>
<Path Data="M0,0 L6,3.5 L0,7 Z" Fill="#ff0000" Width="6" Height="7"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="YPP" Foreground="White" FontWeight="Bold" FontSize="15"
HorizontalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</StackPanel>
</Border>
<Border Width="30" VerticalAlignment="Center" Background="#ffffff"
<Border Width="30" VerticalAlignment="Center" Background="#3b82f6"
CornerRadius="4,0,0,4" Padding="0,10" Cursor="Hand" Margin="0,4,0,0"
ToolTip="Add or manage your social handles">
<Border.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding OpenSocialDialogCommand}"/>
</Border.InputBindings>
<StackPanel HorizontalAlignment="Center">
<Path Data="M4,4 m-2,0 a2,2 0 1 0 4,0 a2,2 0 1 0 -4,0 M16,8 m-2,0 a2,2 0 1 0 4,0 a2,2 0 1 0 -4,0 M4,14 m-2,0 a2,2 0 1 0 4,0 a2,2 0 1 0 -4,0 M5.8,5.2 L14.2,7.2 M5.8,12.8 L14.2,8.8"
Stroke="#0b1326" StrokeThickness="1.5" Width="18" Height="18" Stretch="Uniform"
HorizontalAlignment="Center" Margin="0,0,0,6">
<Path.LayoutTransform><RotateTransform Angle="90"/></Path.LayoutTransform></Path>
<TextBlock Text="Socials" Foreground="#0b1326" FontWeight="Bold" FontSize="15"
HorizontalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</StackPanel>
</Border>
<Border Width="30" VerticalAlignment="Center" Background="#ff8c3a"
CornerRadius="4,0,0,4" Padding="0,10" Cursor="Hand" Margin="0,4,0,0"
ToolTip="Test stream — mock chat input and simulated events">
<Border.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding TestSession.ToggleDrawerCommand}"/>
</Border.InputBindings>
<TextBlock Text="TEST" Foreground="#9c6f1c" FontWeight="Bold" FontSize="15"
<StackPanel HorizontalAlignment="Center">
<Path Data="M8,2 H16 M9,2 V8 L4,18 H20 L15,8 V2 M7,14 H17"
Stroke="#0b1326" StrokeThickness="1.5" Width="18" Height="18" Stretch="Uniform"
HorizontalAlignment="Center" Margin="0,0,0,6">
<Path.LayoutTransform><RotateTransform Angle="90"/></Path.LayoutTransform></Path>
<TextBlock Text="TEST" Foreground="#0b1326" FontWeight="Bold" FontSize="15"
HorizontalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
@@ -803,20 +944,6 @@
VerticalAlignment="Center" Margin="0,4">
<!-- Line 1: Socials + TRAX -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,4">
<!-- Socials button: hover glows the social bar in the preview -->
<Button x:Name="SocialsButton"
Command="{Binding OpenSocialDialogCommand}"
Style="{StaticResource YtButtonSecondary}" Padding="12,4"
FontSize="11" VerticalAlignment="Center" Margin="0,0,12,0"
ToolTip="Add or manage your social handles"
MouseEnter="SocialsButton_MouseEnter"
MouseLeave="SocialsButton_MouseLeave">
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Fill="{Binding SocialBarDotBrush}"
Margin="0,0,6,0" VerticalAlignment="Center"/>
<TextBlock Text="Socials"/>
</StackPanel>
</Button>
<!-- TRAX meter -->
<Border Background="#D9000000" CornerRadius="6" Padding="12,8">
<StackPanel Orientation="Horizontal">
+4 -6
View File
@@ -62,12 +62,10 @@
</StackPanel>
</Border>
<TextBlock Text="{Binding HotkeyLabel, Mode=OneWay}" Foreground="White" FontSize="16" FontWeight="SemiBold"
HorizontalAlignment="Right" VerticalAlignment="Top" Margin="6">
<TextBlock.Effect>
<DropShadowEffect BlurRadius="4" ShadowDepth="0" Opacity="0.9"/>
</TextBlock.Effect>
</TextBlock>
<Border HorizontalAlignment="Right" VerticalAlignment="Top" Margin="6" Padding="8,2"
CornerRadius="10" Background="#000000">
<TextBlock Text="{Binding HotkeyLabel, Mode=OneWay}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</Border>
<TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="White" FontSize="16" FontWeight="SemiBold"
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="6"
+10 -8
View File
@@ -58,16 +58,18 @@
<ToggleButton IsChecked="{Binding EditingActive, Mode=TwoWay}" Content="EDIT"
Cursor="Hand" Height="32" Padding="14,0" Margin="0,0,24,0" VerticalAlignment="Center"
FontWeight="Bold" FontSize="12" Foreground="#0b1326" BorderThickness="0">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Background" Value="#38bdf8"/>
<Style.Triggers>
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="EditBody" Background="#38bdf8" CornerRadius="4" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#ff8c3a"/>
<Setter TargetName="EditBody" Property="Background" Value="#ff8c3a"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<!-- IDLE: the mode switch + the action(s) of the armed world. The three
controls share one height (build 77 feedback) so they read as one
+23
View File
@@ -0,0 +1,23 @@
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace ytLive.Helpers;
public class DroppedFramesToBrushConverter : IValueConverter
{
private static readonly SolidColorBrush Attention = new(Color.FromRgb(0xff, 0x8c, 0x3a));
private static readonly SolidColorBrush Clean = new(Colors.White);
static DroppedFramesToBrushConverter()
{
Attention.Freeze();
Clean.Freeze();
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> value is int dropped && dropped > 0 ? Attention : Clean;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
+20
View File
@@ -0,0 +1,20 @@
using System.Globalization;
using System.Windows.Data;
namespace ytLive.Helpers;
public class DroppedPercentConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var dropped = values.Length > 0 && values[0] is int d ? d : 0;
var fps = values.Length > 1 && values[1] is double f ? f : 0;
var duration = values.Length > 2 && values[2] is TimeSpan t ? t : TimeSpan.Zero;
var total = fps * duration.TotalSeconds;
var percent = total > 0 ? dropped / total * 100 : 0;
return $"{dropped} ({percent.ToString("0.0", CultureInfo.InvariantCulture)}%)";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
+55
View File
@@ -31,6 +31,8 @@
<Window.Resources>
<ResourceDictionary>
<Helpers:DroppedFramesToBrushConverter x:Key="DroppedToBrush"/>
<Helpers:DroppedPercentConverter x:Key="DroppedPercent"/>
<!-- Red dot shown in the taskbar icon while live -->
<DrawingImage x:Key="LiveOverlay">
@@ -78,6 +80,59 @@
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<!-- STREAM READOUTS (top-left cell, above the layers) + resolution -->
<Border Grid.Row="0" Grid.Column="0" Background="#0d1117" CornerRadius="4" Padding="10,8" Margin="0,0,8,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Bitrate" Foreground="#94a3b8" FontSize="12" Margin="0,0,12,2"/>
<TextBlock Grid.Row="0" Grid.Column="1" Foreground="#dae2fd" FontSize="13" HorizontalAlignment="Right" Margin="0,0,0,2">
<Run Text="{Binding CurrentHealth.CurrentBitrate, Mode=OneWay, StringFormat={}{0:0.0}}"/><Run Text=" Mbps"/>
</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" Text="FPS" Foreground="#94a3b8" FontSize="12" Margin="0,0,12,2"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding CurrentHealth.FPS, Mode=OneWay, StringFormat={}{0:0}}"
Foreground="#dae2fd" FontSize="13" HorizontalAlignment="Right" Margin="0,0,0,2"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Dropped" Foreground="#94a3b8" FontSize="12" Margin="0,0,12,2"/>
<TextBlock Grid.Row="2" Grid.Column="1" FontSize="13" HorizontalAlignment="Right" Margin="0,0,0,2"
Foreground="{Binding CurrentHealth.DroppedFrames, Converter={StaticResource DroppedToBrush}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource DroppedPercent}">
<Binding Path="CurrentHealth.DroppedFrames" Mode="OneWay"/>
<Binding Path="CurrentHealth.FPS" Mode="OneWay"/>
<Binding Path="CurrentHealth.StreamDuration" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Live:" Foreground="#94a3b8" FontSize="12" Margin="0,0,12,2"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding CurrentHealth.StreamDuration, Mode=OneWay, StringFormat={}{0:hh\:mm\:ss}}"
Foreground="#ff8c3a" FontSize="13" FontWeight="SemiBold" HorizontalAlignment="Right" Margin="0,0,0,2"/>
<TextBlock Grid.Row="4" Grid.ColumnSpan="2" Text="{Binding CurrentHealth.HealthMessage}"
Foreground="#ff8c3a" FontSize="12" TextWrapping="Wrap" Margin="0,0,0,6"/>
<ComboBox Grid.Row="5" Grid.ColumnSpan="2" VerticalAlignment="Bottom" Style="{StaticResource YtComboBox}"
ItemsSource="{Binding QualityOptions}"
SelectedItem="{Binding SelectedQuality}"
DisplayMemberPath="Label"
ToolTip="{Binding ResolutionHelp}"
IsEnabled="{Binding IsOffline}"/>
</Grid>
</Border>
<!-- ═══ THUMBNAIL STRIP (over the preview column; left column stays empty) ═══ -->
<controls:SceneThumbnailStrip x:Name="SceneStrip" Grid.Row="0" Grid.Column="1" Margin="8,0" Height="154"/>
+7 -11
View File
@@ -1,18 +1,14 @@
<Window x:Class="ytLive.SocialsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Social Media Site Promotion" Height="520" Width="520"
Icon="/Assets/llamacasty-icon.png"
WindowStartupLocation="CenterOwner" ResizeMode="NoResize"
ShowInTaskbar="False" Background="#1a1a2e">
<UserControl x:Class="ytLive.SocialsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<UserControl.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</ResourceDictionary>
</Window.Resources>
</UserControl.Resources>
<Grid Margin="24">
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
@@ -125,4 +121,4 @@
<Button Content="Save" Command="{Binding SaveCommand}" Style="{StaticResource YtButton}" MinWidth="110"/>
</StackPanel>
</Grid>
</Window>
</UserControl>
+4 -10
View File
@@ -1,24 +1,18 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ytLive.Helpers;
using ytLive.ViewModels;
namespace ytLive;
public partial class SocialsDialog : Window
public partial class SocialsDialog : UserControl
{
private readonly SocialsDialogViewModel _viewModel;
private SocialsDialogViewModel _viewModel => (SocialsDialogViewModel)DataContext;
public SocialsDialog(SocialsDialogViewModel viewModel)
public SocialsDialog()
{
AppLog.Write("SocialsDialog ctor: before InitializeComponent");
InitializeComponent();
AppLog.Write("SocialsDialog ctor: after InitializeComponent");
DataContext = viewModel;
_viewModel = viewModel;
viewModel.SaveRequested += () => DialogResult = true;
viewModel.CancelRequested += () => DialogResult = false;
Closing += (_, _) => _viewModel.AbortPending();
}
private void SignInButton_Click(object sender, RoutedEventArgs e)
+29 -3
View File
@@ -119,20 +119,46 @@ public partial class MainViewModel
/// Opens the Social Media Site Promotion dialog (6 slots, sign-in gate,
/// validation). On save the working copy replaces <see cref="_socials"/>.
/// </summary>
/// <summary>The Socials pull-out (same drawer pattern as Stream Settings / YPP / TEST).</summary>
public bool SocialsDrawerOpen { get; private set; }
public SocialsDialogViewModel? SocialsDrawer { get; private set; }
private void OpenSocialDialog()
{
if (SocialsDrawerOpen)
{
CloseSocialsDrawer();
return;
}
var dialog = new SocialsDialogViewModel(
_socialValidator,
SignInAsync,
SignOutYouTubeAsync,
_youtubeAuth.CurrentChannel,
_socials);
var window = new ytLive.SocialsDialog(dialog) { Owner = Application.Current.MainWindow };
if (window.ShowDialog() == true)
dialog.SaveRequested += () =>
{
_socials = dialog.BuildSocialsConfig();
NotifySocialsChanged();
}
CloseSocialsDrawer();
};
dialog.CancelRequested += CloseSocialsDrawer;
SocialsDrawer = dialog;
BroadcastForm.IsDrawerOpen = false;
Ypp.IsDrawerOpen = false;
TestSession.IsDrawerOpen = false;
SocialsDrawerOpen = true;
OnPropertyChanged(nameof(SocialsDrawer));
OnPropertyChanged(nameof(SocialsDrawerOpen));
}
internal void CloseSocialsDrawer()
{
if (!SocialsDrawerOpen) return;
SocialsDrawerOpen = false;
SocialsDrawer?.AbortPending();
OnPropertyChanged(nameof(SocialsDrawerOpen));
}
private void NotifySocialsChanged()
+1
View File
@@ -364,6 +364,7 @@ public partial class MainViewModel : ViewModelBase
new("1080p30", 30, 8.0, 1920, 1080),
new("720p60", 60, 6.0, 1280, 720),
new("720p30", 30, 6.0, 1280, 720),
new("Vertical 1080p30", 30, 8.0, 1080, 1920),
new("Vertical 1080p60", 60, 8.0, 1080, 1920),
};
+3
View File
@@ -38,6 +38,7 @@ public partial class MainViewModel
{
Ypp.IsDrawerOpen = false;
TestSession.IsDrawerOpen = false;
CloseSocialsDrawer();
}
};
Ypp.PropertyChanged += (_, e) =>
@@ -46,6 +47,7 @@ public partial class MainViewModel
{
BroadcastForm.IsDrawerOpen = false;
TestSession.IsDrawerOpen = false;
CloseSocialsDrawer();
}
};
TestSession.PropertyChanged += (_, e) =>
@@ -54,6 +56,7 @@ public partial class MainViewModel
{
BroadcastForm.IsDrawerOpen = false;
Ypp.IsDrawerOpen = false;
CloseSocialsDrawer();
}
};
}