creator feedback batch: 9 fixes from initial review
1. About box bg → #0A0C1A (matches icon bg) 2. Elements panel collapses when no element selected / scene changes 3. OpacityChip moved from preview overlay into Elements panel 4. Color picker fix: cast to SceneElement not Source (WebcamSceneConfig was silently blocked) 5. Red left accent bar on selected items in Scene/Source lists 6. ✓ character replaced with Path checkmark icon in Elements panel 7. Removed ToolTip='Applied' from green check buttons 8. Created bugs.md with first entry (iTunes audio on mic meter) 9. Full logo in README + About box uses ytLlive-logo.png
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace ytLive.Helpers;
|
||||||
|
|
||||||
|
public class NullToVisibilityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
=> value == null ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
=> value is Visibility.Collapsed;
|
||||||
|
}
|
||||||
+52
-56
@@ -33,6 +33,8 @@
|
|||||||
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
||||||
<Helpers:NotNullToVisibilityConverter x:Key="NotNullToVis"
|
<Helpers:NotNullToVisibilityConverter x:Key="NotNullToVis"
|
||||||
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
||||||
|
<Helpers:NullToVisibilityConverter x:Key="NullToVis"
|
||||||
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
||||||
<Helpers:HiddenBoolToVisibilityConverter x:Key="HiddenBoolToVis"
|
<Helpers:HiddenBoolToVisibilityConverter x:Key="HiddenBoolToVis"
|
||||||
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
xmlns:Helpers="clr-namespace:ytLive.Helpers"/>
|
||||||
<Helpers:EnumToBoolConverter x:Key="EnumToBool"
|
<Helpers:EnumToBoolConverter x:Key="EnumToBool"
|
||||||
@@ -356,13 +358,40 @@
|
|||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
||||||
|
|
||||||
<!-- ELEMENTS panel: per-element config beneath Sources -->
|
<!-- ELEMENTS panel: per-element config beneath Sources.
|
||||||
<Rectangle Grid.Row="5" Height="1" Fill="#2c3a5c" Margin="0,10,0,2"/>
|
Collapses entirely when no element is selected. -->
|
||||||
<StackPanel Grid.Row="6" Orientation="Horizontal" Margin="0,8,0,0">
|
<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="ELEMENTS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
<TextBlock Text="ELEMENTS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ScrollViewer Grid.Row="7" VerticalScrollBarVisibility="Auto" Background="Transparent">
|
<ScrollViewer Grid.Row="7" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||||
|
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
||||||
<StackPanel x:Name="ElementsPanel" Margin="0,4,0,0">
|
<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">
|
||||||
|
<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 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 -->
|
<!-- Webcam border config -->
|
||||||
<StackPanel Visibility="{Binding SelectedElement.IsWebcam, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
|
<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"/>
|
<TextBlock Text="Border Color" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"/>
|
||||||
@@ -384,9 +413,11 @@
|
|||||||
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
|
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<DockPanel Margin="0,4,0,0">
|
<DockPanel Margin="0,4,0,0">
|
||||||
<Button DockPanel.Dock="Right" Content="✓" Width="22" Height="22"
|
<Button DockPanel.Dock="Right" Width="22" Height="22"
|
||||||
Background="#22c55e" Foreground="White" FontSize="11" FontWeight="Bold"
|
Background="#22c55e" IsEnabled="False" Margin="4,0,0,0">
|
||||||
ToolTip="Applied" IsEnabled="False" Margin="4,0,0,0"/>
|
<Path Data="M4,9 L8,14 L16,4" Stroke="White" StrokeThickness="2"
|
||||||
|
Stretch="Uniform" Width="12" Height="12"/>
|
||||||
|
</Button>
|
||||||
<Button DockPanel.Dock="Right" Content="✕" Width="22" Height="22"
|
<Button DockPanel.Dock="Right" Content="✕" Width="22" Height="22"
|
||||||
Background="#ef4444" Foreground="White" FontSize="11" FontWeight="Bold"
|
Background="#ef4444" Foreground="White" FontSize="11" FontWeight="Bold"
|
||||||
ToolTip="Revert" Click="WebcamBorderRevert_Click" Margin="4,0,0,0"/>
|
ToolTip="Revert" Click="WebcamBorderRevert_Click" Margin="4,0,0,0"/>
|
||||||
@@ -404,9 +435,11 @@
|
|||||||
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
|
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<DockPanel Margin="0,4,0,0">
|
<DockPanel Margin="0,4,0,0">
|
||||||
<Button DockPanel.Dock="Right" Content="✓" Width="22" Height="22"
|
<Button DockPanel.Dock="Right" Width="22" Height="22"
|
||||||
Background="#22c55e" Foreground="White" FontSize="11" FontWeight="Bold"
|
Background="#22c55e" IsEnabled="False" Margin="4,0,0,0">
|
||||||
ToolTip="Applied" IsEnabled="False" Margin="4,0,0,0"/>
|
<Path Data="M4,9 L8,14 L16,4" Stroke="White" StrokeThickness="2"
|
||||||
|
Stretch="Uniform" Width="12" Height="12"/>
|
||||||
|
</Button>
|
||||||
<Button DockPanel.Dock="Right" Content="✕" Width="22" Height="22"
|
<Button DockPanel.Dock="Right" Content="✕" Width="22" Height="22"
|
||||||
Background="#ef4444" Foreground="White" FontSize="11" FontWeight="Bold"
|
Background="#ef4444" Foreground="White" FontSize="11" FontWeight="Bold"
|
||||||
ToolTip="Revert" Click="CountdownRevert_Click" Margin="4,0,0,0"/>
|
ToolTip="Revert" Click="CountdownRevert_Click" Margin="4,0,0,0"/>
|
||||||
@@ -421,9 +454,11 @@
|
|||||||
BorderBrush="#2c3a5c" Padding="4,2" FontSize="12"
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="12"
|
||||||
ToolTip="https://example.com"/>
|
ToolTip="https://example.com"/>
|
||||||
<DockPanel Margin="0,4,0,0">
|
<DockPanel Margin="0,4,0,0">
|
||||||
<Button DockPanel.Dock="Right" Content="✓" Width="22" Height="22"
|
<Button DockPanel.Dock="Right" Width="22" Height="22"
|
||||||
Background="#22c55e" Foreground="White" FontSize="11" FontWeight="Bold"
|
Background="#22c55e" IsEnabled="False" Margin="4,0,0,0">
|
||||||
ToolTip="Applied" IsEnabled="False" Margin="4,0,0,0"/>
|
<Path Data="M4,9 L8,14 L16,4" Stroke="White" StrokeThickness="2"
|
||||||
|
Stretch="Uniform" Width="12" Height="12"/>
|
||||||
|
</Button>
|
||||||
<Button DockPanel.Dock="Right" Content="✕" Width="22" Height="22"
|
<Button DockPanel.Dock="Right" Content="✕" Width="22" Height="22"
|
||||||
Background="#ef4444" Foreground="White" FontSize="11" FontWeight="Bold"
|
Background="#ef4444" Foreground="White" FontSize="11" FontWeight="Bold"
|
||||||
ToolTip="Revert" Click="WebRevert_Click" Margin="4,0,0,0"/>
|
ToolTip="Revert" Click="WebRevert_Click" Margin="4,0,0,0"/>
|
||||||
@@ -432,21 +467,8 @@
|
|||||||
|
|
||||||
<!-- Placeholder hint when no config available -->
|
<!-- Placeholder hint when no config available -->
|
||||||
<TextBlock Text="No configurable elements" Foreground="#555" FontSize="11"
|
<TextBlock Text="No configurable elements" Foreground="#555" FontSize="11"
|
||||||
HorizontalAlignment="Center" Margin="0,8,0,0">
|
HorizontalAlignment="Center" Margin="0,8,0,0"
|
||||||
<TextBlock.Style>
|
Visibility="{Binding IsElementsPanelEmpty, Converter={StaticResource BoolToVis}}"/>
|
||||||
<Style TargetType="TextBlock">
|
|
||||||
<Setter Property="Visibility" Value="Collapsed"/>
|
|
||||||
<Style.Triggers>
|
|
||||||
<MultiDataTrigger>
|
|
||||||
<MultiDataTrigger.Conditions>
|
|
||||||
<Condition Binding="{Binding SelectedElement}" Value="{x:Null}"/>
|
|
||||||
</MultiDataTrigger.Conditions>
|
|
||||||
<Setter Property="Visibility" Value="Visible"/>
|
|
||||||
</MultiDataTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</TextBlock.Style>
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -845,32 +867,6 @@
|
|||||||
HorizontalAlignment="Center"/>
|
HorizontalAlignment="Center"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Border x:Name="OpacityChip" HorizontalAlignment="Left" VerticalAlignment="Bottom"
|
|
||||||
Background="#D9000000" CornerRadius="6" Padding="10,8" Margin="12,0,0,12"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
<StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<TextBlock Text="Opacity" Foreground="#e0e0e0" FontSize="13" VerticalAlignment="Center"/>
|
|
||||||
<Slider x:Name="OpacitySlider" Width="120" Minimum="0" Maximum="1" Margin="10,0,0,0"
|
|
||||||
Value="{Binding SelectedElement.Opacity, Mode=TwoWay}"
|
|
||||||
ValueChanged="OpacitySlider_ValueChanged" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock x:Name="OpacityValueText" Text="100%" Foreground="#e0e0e0" FontSize="13"
|
|
||||||
Width="40" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0"
|
|
||||||
Visibility="{Binding IsWebcamSelected, Converter={StaticResource BoolToVis}}">
|
|
||||||
<Button Content="{Binding 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="11" Margin="0,4,0,0"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Desktop/game audio bar: overlaid at the bottom of the
|
<!-- Desktop/game audio bar: overlaid at the bottom of the
|
||||||
preview, always visible. Meter + mute + volume control
|
preview, always visible. Meter + mute + volume control
|
||||||
live here; desktop audio itself is automatic WASAPI
|
live here; desktop audio itself is automatic WASAPI
|
||||||
@@ -1177,7 +1173,7 @@
|
|||||||
</Button.Template>
|
</Button.Template>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Border Background="#1a1a2e" CornerRadius="8" Width="520" MaxHeight="560"
|
<Border Background="#0A0C1A" CornerRadius="8" Width="520" MaxHeight="560"
|
||||||
VerticalAlignment="Center" HorizontalAlignment="Center"
|
VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||||
Padding="24" BorderBrush="#333" BorderThickness="1">
|
Padding="24" BorderBrush="#333" BorderThickness="1">
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -1256,7 +1252,7 @@
|
|||||||
|
|
||||||
<!-- Hub view -->
|
<!-- Hub view -->
|
||||||
<StackPanel Visibility="{Binding IsLicensesOpen, Converter={StaticResource InverseBoolToVis}}">
|
<StackPanel Visibility="{Binding IsLicensesOpen, Converter={StaticResource InverseBoolToVis}}">
|
||||||
<Image Source="/ytLive;component/Assets/tyllive-icon.png" Height="140" Stretch="Uniform"
|
<Image Source="/ytLive;component/Assets/ytLlive-logo.png" Height="100" Stretch="Uniform"
|
||||||
HorizontalAlignment="Center" Margin="0,0,0,12"/>
|
HorizontalAlignment="Center" Margin="0,0,0,12"/>
|
||||||
<TextBlock Text="ytLlive" FontSize="22" FontWeight="Bold" Foreground="#e94560"
|
<TextBlock Text="ytLlive" FontSize="22" FontWeight="Bold" Foreground="#e94560"
|
||||||
HorizontalAlignment="Center"/>
|
HorizontalAlignment="Center"/>
|
||||||
|
|||||||
+3
-6
@@ -209,7 +209,7 @@ public partial class MainWindow : Window
|
|||||||
if (e.OriginalSource is not DependencyObject original) return;
|
if (e.OriginalSource is not DependencyObject original) return;
|
||||||
if (IsDescendantOf(original, PreviewGrid)) return;
|
if (IsDescendantOf(original, PreviewGrid)) return;
|
||||||
if (IsDescendantOf(original, SourceList)) return;
|
if (IsDescendantOf(original, SourceList)) return;
|
||||||
if (IsDescendantOf(original, OpacityChip)) return;
|
if (IsDescendantOf(original, ElementsPanel)) return;
|
||||||
_viewModel.SelectedElement = null;
|
_viewModel.SelectedElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +224,6 @@ public partial class MainWindow : Window
|
|||||||
{
|
{
|
||||||
var selected = _viewModel.SelectedElement is { } s && IsDraggableElement(s);
|
var selected = _viewModel.SelectedElement is { } s && IsDraggableElement(s);
|
||||||
SelectionOverlay.Visibility = selected ? Visibility.Visible : Visibility.Collapsed;
|
SelectionOverlay.Visibility = selected ? Visibility.Visible : Visibility.Collapsed;
|
||||||
OpacityChip.Visibility = selected ? Visibility.Visible : Visibility.Collapsed;
|
|
||||||
if (selected)
|
if (selected)
|
||||||
OpacityValueText.Text = $"{Math.Round(_viewModel.SelectedElement!.Opacity * 100)}%";
|
OpacityValueText.Text = $"{Math.Round(_viewModel.SelectedElement!.Opacity * 100)}%";
|
||||||
TakeSnapshot();
|
TakeSnapshot();
|
||||||
@@ -232,7 +231,7 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
// Static images and the webcam share the move/resize/selection behavior.
|
// Static images and the webcam share the move/resize/selection behavior.
|
||||||
private static bool IsDraggableElement(SceneElement element)
|
private static bool IsDraggableElement(SceneElement element)
|
||||||
=> element is Source { Type: SourceType.Image } or WebcamSceneConfig;
|
=> element.IsDraggable;
|
||||||
|
|
||||||
private void MirrorButton_Click(object sender, RoutedEventArgs e)
|
private void MirrorButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -304,8 +303,6 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void Preview_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void Preview_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (OpacityChip.IsMouseOver) return;
|
|
||||||
|
|
||||||
var grid = (Grid)sender;
|
var grid = (Grid)sender;
|
||||||
var toCanvas = CanvasGrid.TransformToVisual(grid).Inverse;
|
var toCanvas = CanvasGrid.TransformToVisual(grid).Inverse;
|
||||||
var p = toCanvas.Transform(e.GetPosition(grid));
|
var p = toCanvas.Transform(e.GetPosition(grid));
|
||||||
@@ -538,7 +535,7 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void WebcamBorderColorPicker_Click(object sender, RoutedEventArgs e)
|
private void WebcamBorderColorPicker_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_viewModel.SelectedElement is not Models.Source src) return;
|
if (_viewModel.SelectedElement is not SceneElement src) return;
|
||||||
if (sender is FrameworkElement fe && fe.ContextMenu == null)
|
if (sender is FrameworkElement fe && fe.ContextMenu == null)
|
||||||
{
|
{
|
||||||
var menu = new System.Windows.Controls.ContextMenu();
|
var menu = new System.Windows.Controls.ContextMenu();
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ public abstract class SceneElement : INotifyPropertyChanged
|
|||||||
/// <summary>True only for singleton resources (webcam). Gates webcam-only UI.</summary>
|
/// <summary>True only for singleton resources (webcam). Gates webcam-only UI.</summary>
|
||||||
public virtual bool IsWebcam => false;
|
public virtual bool IsWebcam => false;
|
||||||
|
|
||||||
|
/// <summary>True for elements that can be dragged/resized in the preview (image, webcam).</summary>
|
||||||
|
public virtual bool IsDraggable => false;
|
||||||
|
|
||||||
/// <summary>True only for image sources — the raw preview renderer's discriminator.</summary>
|
/// <summary>True only for image sources — the raw preview renderer's discriminator.</summary>
|
||||||
public virtual bool IsImageSource => false;
|
public virtual bool IsImageSource => false;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ public class Source : SceneElement
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsDraggable => Type == SourceType.Image;
|
||||||
|
|
||||||
/// <summary>The permanent scene backdrop (live desktop/game capture). Never removable/reorderable.</summary>
|
/// <summary>The permanent scene backdrop (live desktop/game capture). Never removable/reorderable.</summary>
|
||||||
private bool _isBackdrop;
|
private bool _isBackdrop;
|
||||||
public bool IsBackdrop { get => _isBackdrop; set => Set(ref _isBackdrop, value); }
|
public bool IsBackdrop { get => _isBackdrop; set => Set(ref _isBackdrop, value); }
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ public class WebcamSceneConfig : SceneElement
|
|||||||
{
|
{
|
||||||
public string WebcamId { get; init; } = string.Empty;
|
public string WebcamId { get; init; } = string.Empty;
|
||||||
public override bool IsWebcam => true;
|
public override bool IsWebcam => true;
|
||||||
|
public override bool IsDraggable => true;
|
||||||
public override ImageSource? DisplaySource => VideoImageSource;
|
public override ImageSource? DisplaySource => VideoImageSource;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# ytLlive
|
# ytLlive
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="Assets/ytLlive-logo.png" alt="ytLlive — livestream smarter" height="120"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
A creator-proof live-streaming and recording app for YouTube — native Windows (C# / WPF / .NET 8).
|
A creator-proof live-streaming and recording app for YouTube — native Windows (C# / WPF / .NET 8).
|
||||||
|
|
||||||
## Screenshot
|
## Screenshot
|
||||||
|
|||||||
+11
-5
@@ -519,17 +519,23 @@
|
|||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="ListBoxItem">
|
<ControlTemplate TargetType="ListBoxItem">
|
||||||
<Border x:Name="Bd" Background="{TemplateBinding Background}"
|
<Grid>
|
||||||
CornerRadius="3" Padding="{TemplateBinding Padding}" Margin="0,1">
|
<Border x:Name="Accent" Width="3" HorizontalAlignment="Left" CornerRadius="1"
|
||||||
<ContentPresenter VerticalAlignment="Center"
|
Background="#e94560" Margin="0,2" Opacity="0"
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
|
VerticalAlignment="Stretch"/>
|
||||||
</Border>
|
<Border x:Name="Bd" Background="{TemplateBinding Background}"
|
||||||
|
CornerRadius="3" Padding="{TemplateBinding Padding}" Margin="0,1">
|
||||||
|
<ContentPresenter VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
<ControlTemplate.Triggers>
|
<ControlTemplate.Triggers>
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
<Setter TargetName="Bd" Property="Background" Value="#22ffffff"/>
|
<Setter TargetName="Bd" Property="Background" Value="#22ffffff"/>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
<Trigger Property="IsSelected" Value="True">
|
<Trigger Property="IsSelected" Value="True">
|
||||||
<Setter TargetName="Bd" Property="Background" Value="#1a3a66"/>
|
<Setter TargetName="Bd" Property="Background" Value="#1a3a66"/>
|
||||||
|
<Setter TargetName="Accent" Property="Opacity" Value="1"/>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
</ControlTemplate.Triggers>
|
</ControlTemplate.Triggers>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
|
|||||||
@@ -213,7 +213,10 @@ public class MainViewModel : ViewModelBase
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedElement, value))
|
if (SetProperty(ref _selectedElement, value))
|
||||||
|
{
|
||||||
OnPropertyChanged(nameof(IsWebcamSelected));
|
OnPropertyChanged(nameof(IsWebcamSelected));
|
||||||
|
OnPropertyChanged(nameof(IsElementsPanelEmpty));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +266,11 @@ public class MainViewModel : ViewModelBase
|
|||||||
/// <summary>Shows the mirror / clip-shape row in the source chip when a webcam is selected.</summary>
|
/// <summary>Shows the mirror / clip-shape row in the source chip when a webcam is selected.</summary>
|
||||||
public bool IsWebcamSelected => SelectedElement is WebcamSceneConfig;
|
public bool IsWebcamSelected => SelectedElement is WebcamSceneConfig;
|
||||||
|
|
||||||
|
/// <summary>True when the Elements panel should show the "No configurable elements" hint.</summary>
|
||||||
|
public bool IsElementsPanelEmpty => SelectedElement is { } el
|
||||||
|
&& !el.IsDraggable && !el.IsWebcam
|
||||||
|
&& el is not Source { Type: SourceType.Countdown or SourceType.WebSource };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The reason the webcam feed is down (device in use, offline, locked, no frames),
|
/// The reason the webcam feed is down (device in use, offline, locked, no frames),
|
||||||
/// or null when it's alive. Surfaced as a red chip so a dead camera is never a
|
/// or null when it's alive. Surfaced as a red chip so a dead camera is never a
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Bugs
|
||||||
|
|
||||||
|
## iTunes music showing on mic meter
|
||||||
|
Playing music via iTunes causes the mic meter to show audio levels. The WASAPI
|
||||||
|
mic capture (`DataFlow.Capture`) and loopback capture (`DataFlow.Loopback`) are
|
||||||
|
separate devices. If iTunes audio appears on the mic meter, it is likely acoustic
|
||||||
|
coupling — the mic picking up speaker/headphone output. Verify by muting the mic;
|
||||||
|
if the meter still shows levels, check Windows Sound settings for stereo mix or
|
||||||
|
"listen to this device" routing.
|
||||||
Reference in New Issue
Block a user