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:
+2
-40
@@ -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>
|
||||
@@ -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
@@ -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">
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
+6
-10
@@ -1,18 +1,14 @@
|
||||
<Window x:Class="ytLive.SocialsDialog"
|
||||
<UserControl 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">
|
||||
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
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user