Fix UI polish: scrollbar deselect, web source inline buttons, countdown in Sources, volume click-to-position, meter decay
- Elements scrollbar no longer deselects element on click (check ScrollViewer, not StackPanel) - Web source: URI + check/revert buttons merged into single DockPanel row, consistent order (✓ rightmost) - Countdown timer moved from Elements panel to inline in source list row, range clamped to 1-30 min - Volume slider click-to-position restored: e.Handled prevents IncreaseLarge from overriding position, Thumb drag still works - Desktop audio meter: asymmetric smoothing (attack 0.2, decay 0.08) for faster response to silence - 207 tests passing, 0 warnings
This commit is contained in:
+29
-37
@@ -278,6 +278,10 @@
|
|||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Margin="0,2">
|
<Grid Margin="0,2">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
@@ -297,7 +301,7 @@
|
|||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</Grid.ContextMenu>
|
</Grid.ContextMenu>
|
||||||
|
|
||||||
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"
|
||||||
Padding="4,2" TextTrimming="CharacterEllipsis">
|
Padding="4,2" TextTrimming="CharacterEllipsis">
|
||||||
<TextBlock.Style>
|
<TextBlock.Style>
|
||||||
<Style TargetType="TextBlock">
|
<Style TargetType="TextBlock">
|
||||||
@@ -311,7 +315,7 @@
|
|||||||
</TextBlock.Style>
|
</TextBlock.Style>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<TextBox Grid.Column="0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
|
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
|
||||||
Background="#0f3460" Foreground="#e0e0e0" BorderThickness="0"
|
Background="#0f3460" Foreground="#e0e0e0" BorderThickness="0"
|
||||||
Padding="4,2" VerticalContentAlignment="Center"
|
Padding="4,2" VerticalContentAlignment="Center"
|
||||||
IsVisibleChanged="SourceNameBox_IsVisibleChanged"
|
IsVisibleChanged="SourceNameBox_IsVisibleChanged"
|
||||||
@@ -328,27 +332,38 @@
|
|||||||
</TextBox.Style>
|
</TextBox.Style>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<Button Grid.Column="1" ToolTip="Edit Source Name"
|
<Button Grid.Row="0" Grid.Column="1" ToolTip="Edit Source Name"
|
||||||
Command="{Binding DataContext.EditElementCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
Command="{Binding DataContext.EditElementCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||||
CommandParameter="{Binding}" Style="{StaticResource IconButton}">
|
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"
|
<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"/>
|
Fill="#d0d0d0" Width="13" Height="13" Stretch="Uniform"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button Grid.Column="2" Style="{StaticResource EyeButton}"
|
<Button Grid.Row="0" Grid.Column="2" Style="{StaticResource EyeButton}"
|
||||||
Command="{Binding DataContext.ToggleElementVisibilityCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
Command="{Binding DataContext.ToggleElementVisibilityCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||||
CommandParameter="{Binding}">
|
CommandParameter="{Binding}">
|
||||||
<Path Style="{StaticResource EyeIconStyle}" Fill="#d0d0d0"
|
<Path Style="{StaticResource EyeIconStyle}" Fill="#d0d0d0"
|
||||||
Width="13" Height="13" Stretch="Uniform"/>
|
Width="13" Height="13" Stretch="Uniform"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button Grid.Column="3" ToolTip="Remove Source"
|
<Button Grid.Row="0" Grid.Column="3" ToolTip="Remove Source"
|
||||||
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||||
CommandParameter="{Binding}" Style="{StaticResource IconButton}"
|
CommandParameter="{Binding}" Style="{StaticResource IconButton}"
|
||||||
Visibility="{Binding IsBackdrop, Converter={StaticResource HiddenBoolToVis}}">
|
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"
|
<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"/>
|
Fill="#d0d0d0" Width="13" Height="13" Stretch="Uniform"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<!-- Countdown timer: inline in source list row 1 -->
|
||||||
|
<DockPanel Grid.Row="1" Grid.ColumnSpan="4" Margin="8,2,4,2"
|
||||||
|
Visibility="{Binding Type, Converter={StaticResource SourceTypeToCountdownVis}, ConverterParameter=Countdown}">
|
||||||
|
<TextBlock Text="{Binding TimerMinutes, StringFormat={}{0} min}" Foreground="#a0a0b0" FontSize="11"
|
||||||
|
DockPanel.Dock="Right" VerticalAlignment="Center" Margin="6,0,0,0" Width="42"/>
|
||||||
|
<Slider DockPanel.Dock="Left" Width="130" Minimum="1" Maximum="30"
|
||||||
|
Value="{Binding TimerMinutes, Mode=TwoWay}"
|
||||||
|
VerticalAlignment="Center" TickFrequency="1" IsSnapToTickEnabled="True"
|
||||||
|
Style="{StaticResource CompactSlider}"/>
|
||||||
|
</DockPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
@@ -366,7 +381,7 @@
|
|||||||
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
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 x:Name="ElementsScrollViewer" Grid.Row="7" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||||
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
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) -->
|
<!-- Opacity + mirror/shape (draggable elements: image, webcam) -->
|
||||||
@@ -425,48 +440,25 @@
|
|||||||
</DockPanel>
|
</DockPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Countdown config -->
|
<!-- Web source config -->
|
||||||
<StackPanel Visibility="{Binding SelectedElement.Type, Converter={StaticResource SourceTypeToCountdownVis}, ConverterParameter=Countdown}" Margin="0,0,0,8">
|
<StackPanel Visibility="{Binding SelectedElement.Type, Converter={StaticResource SourceTypeToWebVis}, ConverterParameter=WebSource}" Margin="0,0,0,8">
|
||||||
<TextBlock Text="Timer (minutes)" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"/>
|
<TextBlock Text="URI" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"/>
|
||||||
<DockPanel Margin="0,0,0,6">
|
<DockPanel>
|
||||||
<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>
|
|
||||||
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
||||||
IsEnabled="False" Width="22" Height="22" Margin="4,0,0,0">
|
IsEnabled="False" Width="22" Height="22" Margin="4,0,0,0">
|
||||||
<Path Data="M4,9 L8,14 L16,4" Stroke="#22c55e" StrokeThickness="2"
|
<Path Data="M4,9 L8,14 L16,4" Stroke="#22c55e" StrokeThickness="2"
|
||||||
Width="14" Height="14" Stretch="Uniform"/>
|
Width="14" Height="14" Stretch="Uniform"/>
|
||||||
</Button>
|
</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.Type, Converter={StaticResource SourceTypeToWebVis}, ConverterParameter=WebSource}" Margin="0,0,0,8">
|
|
||||||
<TextBlock Text="URI" Foreground="#a0a0b0" FontSize="11" Margin="0,0,0,2"/>
|
|
||||||
<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 Margin="0,4,0,0">
|
|
||||||
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
||||||
Width="22" Height="22" ToolTip="Revert"
|
Width="22" Height="22" ToolTip="Revert"
|
||||||
Click="WebRevert_Click" Margin="4,0,0,0">
|
Click="WebRevert_Click" Margin="4,0,0,0">
|
||||||
<Path Data="M5,5 L15,15 M15,5 L5,15" Stroke="#e94560" StrokeThickness="2"
|
<Path Data="M5,5 L15,15 M15,5 L5,15" Stroke="#e94560" StrokeThickness="2"
|
||||||
Width="14" Height="14" Stretch="Uniform"/>
|
Width="14" Height="14" Stretch="Uniform"/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
|
<TextBox Text="{Binding SelectedElement.WebUri, UpdateSourceTrigger=PropertyChanged}"
|
||||||
IsEnabled="False" Width="22" Height="22" Margin="4,0,0,0">
|
Background="#0f3460" Foreground="#e0d0e0" BorderThickness="1"
|
||||||
<Path Data="M4,9 L8,14 L16,4" Stroke="#22c55e" StrokeThickness="2"
|
BorderBrush="#2c3a5c" Padding="4,2" FontSize="12"
|
||||||
Width="14" Height="14" Stretch="Uniform"/>
|
ToolTip="https://example.com"/>
|
||||||
</Button>
|
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|||||||
+18
-3
@@ -83,8 +83,11 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void VolumeSlider_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void VolumeSlider_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is Slider slider)
|
if (sender is Slider slider && ClickIsOnTrackBackground(slider, e))
|
||||||
|
{
|
||||||
SetSliderValueFromClick(slider, e);
|
SetSliderValueFromClick(slider, e);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
if (sender is FrameworkElement { DataContext: MainViewModel vm })
|
if (sender is FrameworkElement { DataContext: MainViewModel vm })
|
||||||
vm.SetVolumeAdjusting(true);
|
vm.SetVolumeAdjusting(true);
|
||||||
}
|
}
|
||||||
@@ -103,8 +106,11 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void GameVolumeSlider_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void GameVolumeSlider_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is Slider slider)
|
if (sender is Slider slider && ClickIsOnTrackBackground(slider, e))
|
||||||
|
{
|
||||||
SetSliderValueFromClick(slider, e);
|
SetSliderValueFromClick(slider, e);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
if (sender is FrameworkElement { DataContext: MainViewModel vm })
|
if (sender is FrameworkElement { DataContext: MainViewModel vm })
|
||||||
vm.SetGameVolumeAdjusting(true);
|
vm.SetGameVolumeAdjusting(true);
|
||||||
}
|
}
|
||||||
@@ -121,6 +127,15 @@ public partial class MainWindow : Window
|
|||||||
vm.SetGameVolumeAdjusting(false);
|
vm.SetGameVolumeAdjusting(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool ClickIsOnTrackBackground(Slider slider, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var track = slider.Template.FindName("PART_Track", slider) as System.Windows.Controls.Primitives.Track;
|
||||||
|
if (track == null) return false;
|
||||||
|
if (track.Thumb != null && IsDescendantOf(e.OriginalSource as DependencyObject, track.Thumb))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetSliderValueFromClick(Slider slider, MouseButtonEventArgs e)
|
private static void SetSliderValueFromClick(Slider slider, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
var track = slider.Template.FindName("PART_Track", slider) as System.Windows.Controls.Primitives.Track;
|
var track = slider.Template.FindName("PART_Track", slider) as System.Windows.Controls.Primitives.Track;
|
||||||
@@ -209,7 +224,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, ElementsPanel)) return;
|
if (IsDescendantOf(original, ElementsScrollViewer)) return;
|
||||||
_viewModel.SelectedElement = null;
|
_viewModel.SelectedElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -96,7 +96,7 @@ public class Source : SceneElement
|
|||||||
public int TimerMinutes
|
public int TimerMinutes
|
||||||
{
|
{
|
||||||
get => _timerMinutes;
|
get => _timerMinutes;
|
||||||
set => Set(ref _timerMinutes, Math.Clamp(value, 1, 60));
|
set => Set(ref _timerMinutes, Math.Clamp(value, 1, 30));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Web source config
|
// Web source config
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
namespace ytLive.Services.Audio;
|
namespace ytLive.Services.Audio;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the smoothed mic level (0..1) from captured samples. Pure —
|
/// Computes the smoothed level (0..1) from captured samples. Pure —
|
||||||
/// unit-tested; the <see cref="AudioMixer"/> only feeds it and forwards the
|
/// unit-tested; the <see cref="AudioMixer"/> only feeds it and forwards the
|
||||||
/// result. RMS-based so it tracks perceived loudness, smoothed so the meter
|
/// result. RMS-based so it tracks perceived loudness, smoothed so the meter
|
||||||
/// does not flicker.
|
/// does not flicker. Supports asymmetric smoothing: fast attack (rise) and
|
||||||
|
/// configurable decay (fall) so the meter responds quickly to silence.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AudioLevelMeter
|
public sealed class AudioLevelMeter
|
||||||
{
|
{
|
||||||
private const float Smoothing = 0.2f;
|
|
||||||
private float _level;
|
private float _level;
|
||||||
|
|
||||||
public float Level => _level;
|
public float Level => _level;
|
||||||
|
|
||||||
|
public AudioLevelMeter(float attackSmoothing = 0.2f, float decaySmoothing = 0.08f)
|
||||||
|
{
|
||||||
|
AttackSmoothing = attackSmoothing;
|
||||||
|
DecaySmoothing = decaySmoothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float AttackSmoothing { get; }
|
||||||
|
public float DecaySmoothing { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps a linear level (0..1) onto the meter's display scale: -60 dBFS..0 dBFS
|
/// Maps a linear level (0..1) onto the meter's display scale: -60 dBFS..0 dBFS
|
||||||
/// spread linearly across 0..1, with +10 dB of input amplification. Linear RMS
|
/// spread linearly across 0..1, with +10 dB of input amplification. Linear RMS
|
||||||
@@ -44,7 +53,8 @@ public sealed class AudioLevelMeter
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rms = (float)Math.Sqrt(sumSquares / count);
|
var rms = (float)Math.Sqrt(sumSquares / count);
|
||||||
_level = _level * (1 - Smoothing) + rms * Smoothing;
|
var smoothing = rms > _level ? AttackSmoothing : DecaySmoothing;
|
||||||
|
_level = _level * (1 - smoothing) + rms * smoothing;
|
||||||
return _level;
|
return _level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public sealed class AudioMixer : IDisposable
|
|||||||
_mic = mic;
|
_mic = mic;
|
||||||
_loopback = loopback;
|
_loopback = loopback;
|
||||||
_meter = new AudioLevelMeter();
|
_meter = new AudioLevelMeter();
|
||||||
_loopbackMeter = new AudioLevelMeter();
|
_loopbackMeter = new AudioLevelMeter(attackSmoothing: 0.2f, decaySmoothing: 0.08f);
|
||||||
_log = log;
|
_log = log;
|
||||||
_micGain = micGain;
|
_micGain = micGain;
|
||||||
_loopbackGain = loopbackGain;
|
_loopbackGain = loopbackGain;
|
||||||
|
|||||||
@@ -818,12 +818,12 @@ No changes to `MusicPlayer`, the 0.20 cap, the ducker, or the meter zones. Build
|
|||||||
1. ✅ TRAX volume — `LocalGain = GameAudioVolume * 0.2` so TRAX is 20% of the desktop audio slider (was 100%, now quieter)
|
1. ✅ TRAX volume — `LocalGain = GameAudioVolume * 0.2` so TRAX is 20% of the desktop audio slider (was 100%, now quieter)
|
||||||
2. ✅ TRAX tooltip — replaced `ToolTip` binding with `ToolTipService.ToolTip` + `Placement="Top"` to fix z-order inside Viewbox overlay
|
2. ✅ TRAX tooltip — replaced `ToolTip` binding with `ToolTipService.ToolTip` + `Placement="Top"` to fix z-order inside Viewbox overlay
|
||||||
3. ✅ Mic meter boost — `MeterLevel` multiplied by 1.2× before clamping so the bar reads ~20% higher at same input level
|
3. ✅ Mic meter boost — `MeterLevel` multiplied by 1.2× before clamping so the bar reads ~20% higher at same input level
|
||||||
4. ✅ Click-to-position sliders — clicking anywhere on the mic/game volume slider track now jumps the thumb to that position before starting the drag
|
4. ✅ Click-to-position sliders — clicking anywhere on the mic/game volume slider track now jumps the thumb to that position before starting the drag (fixed: RepeatButton no longer overrides click position; Thumb drag still works)
|
||||||
5. ✅ Backdrop visibility — the dedicated `<Image>` now binds `Visibility` to `BackdropVisible` (ViewModel property); compositor checks `IsVisible` before blit
|
5. ✅ Backdrop visibility — the dedicated `<Image>` now binds `Visibility` to `BackdropVisible` (ViewModel property); compositor checks `IsVisible` before blit
|
||||||
6. ✅ Rename "Backdrop" → "Game Capture" in `EnsureBackdrop()` + test assertions
|
6. ✅ Rename "Backdrop" → "Game Capture" in `EnsureBackdrop()` + test assertions
|
||||||
7. ✅ Elements panel — new "ELEMENTS" section beneath Sources list; per-element config with live preview, ✕ revert, ✓ applied indicator
|
7. ✅ Elements panel — new "ELEMENTS" section beneath Sources list; per-element config with live preview, ✕ revert, ✓ applied indicator
|
||||||
8. ✅ Webcam border config — color hex input + color swatch picker, border thickness 0–10 slider
|
8. ✅ Webcam border config — color hex input + color swatch picker, border thickness 0–10 slider
|
||||||
9. ✅ Countdown source — `SourceType.Countdown`, pick list (Starting/BRB scenes only), timer minutes 1–60 default 15
|
9. ✅ Countdown source — `SourceType.Countdown`, pick list (Starting/BRB scenes only), timer minutes 1–30 default 15, inline slider in source list row
|
||||||
10. ✅ Web source — `SourceType.WebSource`, pick list (all scenes), URI text input
|
10. ✅ Web source — `SourceType.WebSource`, pick list (all scenes), URI text input
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user