110 hardcoded colours gone from the preview — the biggest surface in the app. Nicknames: - the webcam selection ring is the interactive accent (was the retired brand red) - the mute chips are `attention` (a muted mic is a caution, not a live state) with a white play glyph; the live badge is `primary` - YPP progress bars/track, TRAX level zones, the error line, the dim strips and the poster veil all resolve from tokens (`positive`, `secondary-container`, `error-container`, `scrim-dark`, `scrim-dark-strong`) Plus the duplication this sweep exposed: the webcam's right-click "Border Color" submenu carried **seven hand-written colours**, a second source of truth beside the properties drop-down that TASK 63 had already unified onto `Models/BorderColorPalette.cs` — and the two lists disagreed (7 vs 14). The submenu is now built from the palette when it opens, so every offered colour is the same colour everywhere, with the palette's name beside it. The click handler is unchanged; generated rows inherit the menu's SceneElement DataContext. Guard list extended: Controls/PreviewPane.xaml.
1085 lines
99 KiB
XML
1085 lines
99 KiB
XML
<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:local="clr-namespace:ytLive">
|
||
<Border Background="{StaticResource Brush.SurfaceContainerLowest}" CornerRadius="6" Margin="8,0"
|
||
BorderBrush="{Binding PreviewGlowBrush}" BorderThickness="{Binding PreviewGlowThickness}">
|
||
<Grid>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="*"/>
|
||
<RowDefinition Height="Auto"/>
|
||
</Grid.RowDefinitions>
|
||
|
||
<!-- Preview monitor -->
|
||
<Grid x:Name="PreviewGrid" Grid.Row="0"
|
||
PreviewMouseLeftButtonDown="Preview_MouseLeftButtonDown"
|
||
PreviewMouseMove="Preview_MouseMove"
|
||
PreviewMouseLeftButtonUp="Preview_MouseLeftButtonUp"
|
||
PreviewMouseRightButtonDown="Preview_MouseRightButtonDown"
|
||
PreviewMouseRightButtonUp="Preview_MouseRightButtonUp">
|
||
<Viewbox Stretch="Uniform">
|
||
<Canvas x:Name="PreviewCanvas" Width="1920" Height="1080" ClipToBounds="True">
|
||
<Grid x:Name="CanvasGrid" Width="1920" Height="1080" ClipToBounds="True" Background="Transparent">
|
||
<Grid.RenderTransform>
|
||
<TransformGroup>
|
||
<ScaleTransform x:Name="ZoomScale"/>
|
||
<TranslateTransform x:Name="ZoomTranslate"/>
|
||
</TransformGroup>
|
||
</Grid.RenderTransform>
|
||
<Grid.ContextMenu>
|
||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||
<MenuItem Header="Show Desktop" IsCheckable="True"
|
||
IsChecked="{Binding BackgroundShowDesktop}"
|
||
Visibility="{Binding CanChangeBackground, Converter={StaticResource BoolToVis}}"
|
||
ToolTip="Show desktop when no fullscreen game is detected"/>
|
||
<MenuItem Header="Capture Desktop"
|
||
Visibility="{Binding CanChangeBackground, Converter={StaticResource BoolToVis}}"
|
||
ItemsSource="{Binding Displays}">
|
||
<MenuItem.ItemContainerStyle>
|
||
<Style TargetType="MenuItem">
|
||
<Setter Property="Header" Value="{Binding Label}"/>
|
||
<Setter Property="Command"
|
||
Value="{Binding DataContext.SetBackgroundDisplayCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
||
<Setter Property="CommandParameter" Value="{Binding}"/>
|
||
</Style>
|
||
</MenuItem.ItemContainerStyle>
|
||
</MenuItem>
|
||
<MenuItem Header="Capture Window…"
|
||
Visibility="{Binding CanChangeBackground, Converter={StaticResource BoolToVis}}"
|
||
ItemsSource="{Binding OpenWindows}">
|
||
<MenuItem.ItemContainerStyle>
|
||
<Style TargetType="MenuItem">
|
||
<Setter Property="Header" Value="{Binding Label}"/>
|
||
<Setter Property="Command"
|
||
Value="{Binding DataContext.CaptureWindowCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
||
<Setter Property="CommandParameter" Value="{Binding}"/>
|
||
</Style>
|
||
</MenuItem.ItemContainerStyle>
|
||
</MenuItem>
|
||
<MenuItem Header="Refresh Window List" Command="{Binding RefreshWindowsCommand}"
|
||
Visibility="{Binding CanChangeBackground, Converter={StaticResource BoolToVis}}"/>
|
||
<MenuItem Header="Refresh Desktop" Command="{Binding RefreshCaptureCommand}"
|
||
Visibility="{Binding CanChangeBackground, Converter={StaticResource BoolToVis}}"/>
|
||
</ContextMenu>
|
||
</Grid.ContextMenu>
|
||
<!-- Live desktop/game background (bottom layer; the static
|
||
image renders above it). The zoom box samples the
|
||
whole CanvasGrid, not just this layer (TASK 51). -->
|
||
<Grid x:Name="BackdropLayer" IsHitTestVisible="False">
|
||
<Image Source="{Binding BackgroundImage}" Stretch="UniformToFill"
|
||
Visibility="{Binding BackgroundVisible, Converter={StaticResource BoolToVis}}"/>
|
||
<Image Source="{Binding ActiveBackgroundImage}" Stretch="UniformToFill"/>
|
||
</Grid>
|
||
<Image x:Name="TransitionOverlay" Width="1920" Height="1080" Stretch="Fill"
|
||
IsHitTestVisible="False" Visibility="Collapsed"/>
|
||
<Canvas x:Name="OverlayCanvas" Width="1920" Height="1080">
|
||
<ItemsControl x:Name="OverlayElements" ItemsSource="{Binding StagedScene.Elements}"
|
||
Width="1920" Height="1080">
|
||
<ItemsControl.ItemsPanel>
|
||
<ItemsPanelTemplate>
|
||
<Canvas/>
|
||
</ItemsPanelTemplate>
|
||
</ItemsControl.ItemsPanel>
|
||
<ItemsControl.ItemContainerStyle>
|
||
<Style TargetType="ContentPresenter">
|
||
<Setter Property="Canvas.Left" Value="{Binding X}"/>
|
||
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
|
||
</Style>
|
||
</ItemsControl.ItemContainerStyle>
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate>
|
||
<Grid Width="{Binding Width}" Height="{Binding Height}"
|
||
Opacity="{Binding Opacity}"
|
||
Background="Transparent"
|
||
RenderTransformOrigin="0.5,0.5">
|
||
<Grid.Style>
|
||
<Style TargetType="Grid">
|
||
<Setter Property="Visibility" Value="Collapsed"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding IsVisible}" Value="True">
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</DataTrigger>
|
||
<!-- The background is a fixed full-frame layer (rendered separately,
|
||
below the static background): never hit-testable, so it can't be
|
||
selected, dragged, or bring up the webcam context menu. -->
|
||
<DataTrigger Binding="{Binding IsBackground}" Value="True">
|
||
<Setter Property="IsHitTestVisible" Value="False"/>
|
||
</DataTrigger>
|
||
<!-- The zoom layer never renders here — the ZoomBox magnifier (and the
|
||
output resolver) own its pixels (TASK 51). -->
|
||
<DataTrigger Binding="{Binding IsZoomLayer}" Value="True">
|
||
<Setter Property="Visibility" Value="Collapsed"/>
|
||
<Setter Property="IsHitTestVisible" Value="False"/>
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Grid.Style>
|
||
<Grid.RenderTransform>
|
||
<ScaleTransform ScaleX="{Binding MirrorScale}"/>
|
||
</Grid.RenderTransform>
|
||
<Image Source="{Binding DisplaySource}"
|
||
RenderOptions.BitmapScalingMode="HighQuality">
|
||
<Image.Style>
|
||
<Style TargetType="Image">
|
||
<Setter Property="Visibility" Value="Collapsed"/>
|
||
<Setter Property="Stretch" Value="UniformToFill"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding IsImageSource}" Value="True">
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding IsChatBox}" Value="True">
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</DataTrigger>
|
||
<!-- TASK 47: the alert box's frame (the decoded clip) is written onto
|
||
VideoImageSource exactly like the chat box's, so it needs the same
|
||
trigger. Without it the Image stayed Collapsed and every alert was
|
||
invisible in the preview while the decode logged healthy (2026-09-26). -->
|
||
<DataTrigger Binding="{Binding IsAlertBox}" Value="True">
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding IsWebSource}" Value="True">
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
<Setter Property="Stretch" Value="Fill"/>
|
||
</DataTrigger>
|
||
<MultiDataTrigger>
|
||
<MultiDataTrigger.Conditions>
|
||
<Condition Binding="{Binding IsWebcam}" Value="True"/>
|
||
<Condition Binding="{Binding ClipShape}" Value="Traditional"/>
|
||
</MultiDataTrigger.Conditions>
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</MultiDataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Image.Style>
|
||
</Image>
|
||
<Viewbox Stretch="Uniform">
|
||
<Grid Width="1" Height="1">
|
||
<!-- Round webcam: a plain Image (the same efficient WriteableBitmap
|
||
path as Traditional) clipped to a unit circle and scaled by the
|
||
Viewbox — deliberately NOT an ImageBrush, whose per-frame
|
||
re-rasterization at full scale stalls the preview. -->
|
||
<Image Stretch="UniformToFill"
|
||
Source="{Binding DisplaySource}"
|
||
RenderOptions.BitmapScalingMode="HighQuality">
|
||
<Image.Style>
|
||
<Style TargetType="Image">
|
||
<Setter Property="Visibility" Value="Collapsed"/>
|
||
<Style.Triggers>
|
||
<MultiDataTrigger>
|
||
<MultiDataTrigger.Conditions>
|
||
<Condition Binding="{Binding IsWebcam}" Value="True"/>
|
||
<Condition Binding="{Binding ClipShape}" Value="Round"/>
|
||
</MultiDataTrigger.Conditions>
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</MultiDataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Image.Style>
|
||
<Image.Clip>
|
||
<EllipseGeometry Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5"/>
|
||
</Image.Clip>
|
||
</Image>
|
||
</Grid>
|
||
</Viewbox>
|
||
|
||
|
||
<!-- OBS-style border: stroked rect (Traditional) / centered circle (Round).
|
||
Animations are driven by code-behind in WebcamMenu_SetBorderAnimation
|
||
because DataTrigger storyboards in DataTemplates cannot be stopped
|
||
(no ExitActions namescope support). -->
|
||
<Rectangle IsHitTestVisible="False"
|
||
Stroke="{Binding BorderBrush}"
|
||
StrokeThickness="{Binding BorderWidth}">
|
||
<Rectangle.Style>
|
||
<Style TargetType="Rectangle">
|
||
<Setter Property="Visibility" Value="Collapsed"/>
|
||
<Style.Triggers>
|
||
<MultiDataTrigger>
|
||
<MultiDataTrigger.Conditions>
|
||
<Condition Binding="{Binding HasBorder}" Value="True"/>
|
||
<Condition Binding="{Binding ClipShape}" Value="Traditional"/>
|
||
</MultiDataTrigger.Conditions>
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</MultiDataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Rectangle.Style>
|
||
</Rectangle>
|
||
<Ellipse IsHitTestVisible="False"
|
||
Stroke="{Binding BorderBrush}"
|
||
StrokeThickness="{Binding BorderWidth}"
|
||
Width="{Binding RoundBorderSize}"
|
||
Height="{Binding RoundBorderSize}"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||
<Ellipse.Style>
|
||
<Style TargetType="Ellipse">
|
||
<Setter Property="Visibility" Value="Collapsed"/>
|
||
<Style.Triggers>
|
||
<MultiDataTrigger>
|
||
<MultiDataTrigger.Conditions>
|
||
<Condition Binding="{Binding HasBorder}" Value="True"/>
|
||
<Condition Binding="{Binding ClipShape}" Value="Round"/>
|
||
</MultiDataTrigger.Conditions>
|
||
<Setter Property="Visibility" Value="Visible"/>
|
||
</MultiDataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Ellipse.Style>
|
||
</Ellipse>
|
||
|
||
<Grid.ContextMenu>
|
||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||
<MenuItem Header="Change Webcam…" Click="WebcamMenu_ChangeWebcam"
|
||
Visibility="{Binding IsWebcam, Converter={StaticResource BoolToVis}}"/>
|
||
<Separator/>
|
||
<MenuItem Header="Border Thickness">
|
||
<StackPanel Orientation="Horizontal" Margin="8,2">
|
||
<TextBlock Text="Thickness" Foreground="{StaticResource Brush.OnSurface}" VerticalAlignment="Center"/>
|
||
<Slider Width="120" Minimum="0" Maximum="20" Margin="8,0,0,0"
|
||
Value="{Binding BorderWidth, Mode=TwoWay}"
|
||
VerticalAlignment="Center"/>
|
||
</StackPanel>
|
||
</MenuItem>
|
||
<MenuItem Header="Border Opacity">
|
||
<StackPanel Orientation="Horizontal" Margin="8,2">
|
||
<TextBlock Text="Opacity" Foreground="{StaticResource Brush.OnSurface}" VerticalAlignment="Center"/>
|
||
<Slider Width="120" Minimum="0" Maximum="1" Margin="8,0,0,0"
|
||
Value="{Binding BorderOpacity, Mode=TwoWay}"
|
||
VerticalAlignment="Center"/>
|
||
</StackPanel>
|
||
</MenuItem>
|
||
<!-- Filled from Models/BorderColorPalette on open — the list used to be
|
||
seven hand-written rows here, a second source of truth beside the
|
||
properties drop-down (TASK 63). -->
|
||
<MenuItem x:Name="WebcamBorderColorMenu" Header="Border Color"
|
||
SubmenuOpened="WebcamBorderColorMenu_SubmenuOpened"/>
|
||
<MenuItem Header="Border Effect">
|
||
<MenuItem Header="None" Tag="None" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=None}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Pulse" Tag="Pulse" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Pulse}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Chase" Tag="Chase" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Chase}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Rainbow" Tag="Rainbow" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Rainbow}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Shimmer" Tag="Shimmer" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Shimmer}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Marching Ants" Tag="MarchingAnts" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=MarchingAnts}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Glow" Tag="Glow" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Glow}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Electricity" Tag="Electricity" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Electricity}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
<MenuItem Header="Sparkles" Tag="Sparkles" IsCheckable="True"
|
||
IsChecked="{Binding BorderAnimation, Converter={StaticResource EnumToBool}, ConverterParameter=Sparkles}"
|
||
Click="WebcamMenu_SetBorderAnimation"/>
|
||
</MenuItem>
|
||
<Separator/>
|
||
<MenuItem Header="Hide in this scene" Click="WebcamMenu_HideInScene"/>
|
||
<MenuItem Header="Remove" Click="WebcamMenu_Remove"/>
|
||
</ContextMenu>
|
||
</Grid.ContextMenu>
|
||
</Grid>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
|
||
<Grid x:Name="SelectionOverlay" IsHitTestVisible="False"
|
||
Canvas.Left="{Binding SelectedElement.X}" Canvas.Top="{Binding SelectedElement.Y}"
|
||
Width="{Binding SelectedElement.Width}" Height="{Binding SelectedElement.Height}">
|
||
<Rectangle Stroke="{StaticResource Brush.Attention}" StrokeDashArray="4 3" StrokeThickness="5"/>
|
||
<Ellipse Width="26" Height="26" Fill="{StaticResource Brush.Attention}" Stroke="White" StrokeThickness="2"
|
||
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-13,-13"/>
|
||
</Grid>
|
||
|
||
<Rectangle IsHitTestVisible="False" Stroke="{StaticResource Brush.Tertiary}" StrokeThickness="3"
|
||
Canvas.Left="{Binding OutputRectX}" Canvas.Top="{Binding OutputRectY}"
|
||
Width="{Binding OutputRectWidth}" Height="{Binding OutputRectHeight}"
|
||
Visibility="{Binding IsOutputCropped, Converter={StaticResource BoolToVis}}"/>
|
||
|
||
<!-- Free-tier branding credit. The source is the SAME rendered
|
||
frame FramePump composites into the recording/stream
|
||
(BrandFlashPresenter → MainViewModel.BrandFlashFrame), so
|
||
the preview cannot drift from the broadcast. It replaces a
|
||
preview-only TextBlock that no viewer ever saw, and the
|
||
fade/spread/placement is the renderer's — there is no
|
||
storyboard here to keep in sync. -->
|
||
<Grid x:Name="BrandFlashElement" Width="1920" Height="1080"
|
||
Canvas.Left="0" Canvas.Top="0"
|
||
Background="Transparent" IsHitTestVisible="False"
|
||
Visibility="{Binding BrandFlashVisible, Converter={StaticResource BoolToVis}}">
|
||
<Image Source="{Binding BrandFlashImageSource}"
|
||
Stretch="Fill" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||
</Grid>
|
||
|
||
<!-- Alert announcement strip (TASK 47): the same global
|
||
1920px overlay the output compositor blits along the
|
||
top edge. Global resource — never a Source. Without it
|
||
the strip reached the stream but never the preview. -->
|
||
<!-- Alert ticker: the announcement strip draws INSIDE the Stream
|
||
Alerts box (creator ruling 2026-09-26), so this element is
|
||
placed and sized to that box's rect rather than pinned as a
|
||
1920px bar to the top edge of the preview. -->
|
||
<Grid x:Name="AlertTickerElement"
|
||
Canvas.Left="{Binding AlertTickerLeft}"
|
||
Canvas.Top="{Binding AlertTickerTop}"
|
||
Width="{Binding AlertTickerWidth}"
|
||
Height="{Binding AlertTickerHeight}"
|
||
Background="Transparent" IsHitTestVisible="False"
|
||
Visibility="{Binding AlertTickerVisible, Converter={StaticResource BoolToVis}}">
|
||
<Image Source="{Binding AlertTickerImageSource}"
|
||
Stretch="Fill" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||
</Grid>
|
||
<!-- Social bar: one centered horizontal row at the top or bottom of
|
||
the frame, green glow while enabled. Global resource — never a
|
||
Source, no Sources entry. Click toggles top ⇄ bottom. -->
|
||
<Grid x:Name="SocialBarElement" Width="1920" Canvas.Top="{Binding SocialBarTop}"
|
||
Background="Transparent"
|
||
Visibility="{Binding SocialBarVisible, Converter={StaticResource BoolToVis}}"
|
||
MouseLeftButtonDown="SocialBar_MouseLeftButtonDown"
|
||
Cursor="Hand">
|
||
<Grid.Effect>
|
||
<DropShadowEffect Color="{Binding SocialBarGlowBrush.Color}"
|
||
BlurRadius="18" ShadowDepth="0" Opacity="0.9"/>
|
||
</Grid.Effect>
|
||
<ItemsControl ItemsSource="{Binding Socials.Entries}"
|
||
HorizontalAlignment="Center" Margin="40,8"
|
||
IsHitTestVisible="False">
|
||
<ItemsControl.ItemsPanel>
|
||
<ItemsPanelTemplate>
|
||
<StackPanel Orientation="Horizontal"/>
|
||
</ItemsPanelTemplate>
|
||
</ItemsControl.ItemsPanel>
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate>
|
||
<StackPanel Orientation="Horizontal" Margin="0,0,20,0">
|
||
<Path Data="{Binding LogoData}" Fill="White" Width="24" Height="24"
|
||
Stretch="Uniform" VerticalAlignment="Center"/>
|
||
<TextBlock Text="{Binding Handle}" Foreground="White"
|
||
FontSize="14" VerticalAlignment="Center"
|
||
Margin="8,0,0,0"/>
|
||
</StackPanel>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
</Grid>
|
||
</Canvas>
|
||
</Grid>
|
||
|
||
<!-- The zoom dims: preview chrome ONLY — a SIBLING of CanvasGrid,
|
||
never a descendant — so the zoom box's VisualBrush sample of
|
||
CanvasGrid can never pick up the dim shading. The box must show
|
||
the captured area clean at all times, even when dragged over the
|
||
dims (creator ruling 2026-10-08). Renders above the composed
|
||
content (dims the whole scene outside the box incl. the global
|
||
overlays) and below the ZoomBox. -->
|
||
<ItemsControl x:Name="DimRectsLayer" ItemsSource="{Binding DimRects}" IsHitTestVisible="False">
|
||
<ItemsControl.ItemsPanel>
|
||
<ItemsPanelTemplate>
|
||
<Canvas/>
|
||
</ItemsPanelTemplate>
|
||
</ItemsControl.ItemsPanel>
|
||
<ItemsControl.ItemContainerStyle>
|
||
<Style TargetType="ContentPresenter">
|
||
<Setter Property="Canvas.Left" Value="{Binding X}"/>
|
||
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
|
||
</Style>
|
||
</ItemsControl.ItemContainerStyle>
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate>
|
||
<Rectangle Width="{Binding Width}" Height="{Binding Height}"
|
||
Fill="#000000" Opacity="0.55"/>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
|
||
<!-- The zoom box: layer n+1 — a SIBLING of CanvasGrid (not a
|
||
descendant), so its VisualBrush can sample the whole
|
||
composed preview (CanvasGrid = backdrop + every element
|
||
layer) without a circular reference, and it renders ABOVE
|
||
everything. It shows a fixed capture window (box/2 at the
|
||
click); dragging the box relocates it, never the capture. -->
|
||
<Grid x:Name="ZoomBox" IsHitTestVisible="False"
|
||
Canvas.Left="{Binding ZoomState.X}" Canvas.Top="{Binding ZoomState.Y}"
|
||
Width="{Binding ZoomState.Width}" Height="{Binding ZoomState.Height}"
|
||
Visibility="{Binding IsZoomActive, Converter={StaticResource BoolToVis}}">
|
||
<Rectangle x:Name="ZoomRectOutline" Stroke="{StaticResource Brush.Secondary}" StrokeThickness="3" Fill="Transparent"/>
|
||
<Ellipse x:Name="ZoomCircleOutline" Stroke="{StaticResource Brush.Secondary}" StrokeThickness="3" Fill="Transparent" Visibility="Collapsed"/>
|
||
<Grid.Background>
|
||
<VisualBrush x:Name="ZoomVisualBrush" ViewboxUnits="Absolute" Stretch="Fill"/>
|
||
</Grid.Background>
|
||
</Grid>
|
||
</Canvas>
|
||
</Viewbox>
|
||
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Margin="8,8,0,0"
|
||
Background="{StaticResource Brush.Primary}" CornerRadius="4" Padding="8,3" IsHitTestVisible="False"
|
||
Visibility="{Binding WebcamError, Converter={StaticResource NotNullToVis}}">
|
||
<TextBlock Text="{Binding WebcamError}" Foreground="White" FontSize="12"
|
||
MaxWidth="520" TextTrimming="CharacterEllipsis"/>
|
||
</Border>
|
||
<TextBlock Text="Preview" Foreground="#333" FontSize="24"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||
Visibility="{Binding ShowPreviewPlaceholder, Converter={StaticResource BoolToVis}}"/>
|
||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
|
||
Visibility="{Binding ShowEmptySceneHint, Converter={StaticResource BoolToVis}}">
|
||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||
<TextBlock Text="🖥️" Foreground="White" FontSize="30" Margin="0,0,20,0"/>
|
||
<TextBlock Text="📷" Foreground="White" FontSize="30" Margin="0,0,20,0"/>
|
||
<TextBlock Text="🖼️" Foreground="White" FontSize="30" Margin="0,0,20,0"/>
|
||
<TextBlock Text="✏️" Foreground="White" FontSize="30"/>
|
||
</StackPanel>
|
||
<TextBlock Text="Make it yours!" Foreground="{StaticResource Brush.OnSurface}" FontSize="18"
|
||
FontWeight="SemiBold" HorizontalAlignment="Center" Margin="0,18,0,0"/>
|
||
<TextBlock Text="Add layers — your webcam, your screen, images, and text — to craft a brand and identity that's unmistakably you."
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="13" Margin="0,8,0,0"
|
||
TextWrapping="Wrap" MaxWidth="440" TextAlignment="Center"
|
||
HorizontalAlignment="Center"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
|
||
<!-- ═══ RIGHT-EDGE PULL-OUTS (always visible) ═══
|
||
White "Stream Settings" + "YPP" tabs stacked on the preview's right
|
||
edge; each toggles its own drawer (the same animated-width pattern).
|
||
Only one drawer is open at a time — IsDrawerOpen exclusivity is
|
||
VM-enforced (opening one collapses the other). Any click outside
|
||
the rail collapses whichever is open (MainWindow code-behind). -->
|
||
<Grid Grid.Row="0">
|
||
<StackPanel x:Name="TextPullOutHost" Orientation="Horizontal"
|
||
HorizontalAlignment="Right" VerticalAlignment="Stretch">
|
||
<!-- The broadcast-metadata drawer -->
|
||
<Border Background="{StaticResource Brush.SurfaceContainer}" ClipToBounds="True">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Width" Value="0"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding BroadcastForm.IsDrawerOpen}" 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>
|
||
<!-- Fixed-width inner so text never reflows mid-animation -->
|
||
<Grid Width="320" Margin="14,10,14,12">
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="*"/>
|
||
<RowDefinition Height="Auto"/>
|
||
</Grid.RowDefinitions>
|
||
<Grid Grid.Row="0" Margin="0,0,0,8">
|
||
<TextBlock Text="BROADCAST TEXT" Style="{StaticResource SectionHeader}"
|
||
HorizontalAlignment="Left"/>
|
||
<Button HorizontalAlignment="Right" VerticalAlignment="Top"
|
||
Background="Transparent" BorderThickness="0" Cursor="Hand"
|
||
Command="{Binding BroadcastForm.CloseDrawerCommand}"
|
||
ToolTip="Close">
|
||
<Button.Template>
|
||
<ControlTemplate TargetType="Button">
|
||
<ContentPresenter/>
|
||
</ControlTemplate>
|
||
</Button.Template>
|
||
<TextBlock Text="✕" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="14" FontWeight="Bold"/>
|
||
</Button>
|
||
</Grid>
|
||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||
<StackPanel>
|
||
<TextBlock Text="TITLE" Style="{StaticResource YtLabel}" Margin="0,0,0,3"/>
|
||
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,10"
|
||
Text="{Binding BroadcastForm.Title, UpdateSourceTrigger=PropertyChanged}"/>
|
||
<TextBlock Text="DESCRIPTION" Style="{StaticResource YtLabel}" Margin="0,0,0,3"/>
|
||
<TextBox Style="{StaticResource YtTextBox}" Height="90" AcceptsReturn="True"
|
||
TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Margin="0,0,0,10"
|
||
Text="{Binding BroadcastForm.Description, UpdateSourceTrigger=PropertyChanged}"/>
|
||
<TextBlock Text="TAGS (COMMA-SEPARATED)" Style="{StaticResource YtLabel}" Margin="0,0,0,3"/>
|
||
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,10"
|
||
Text="{Binding BroadcastForm.TagsCsv, UpdateSourceTrigger=PropertyChanged}"/>
|
||
<TextBlock Text="VISIBILITY" Style="{StaticResource YtLabel}" Margin="0,0,0,3"/>
|
||
<ComboBox Style="{StaticResource YtComboBox}" Margin="0,0,0,10"
|
||
ItemsSource="{Binding BroadcastForm.VisibilityOptions}"
|
||
SelectedItem="{Binding BroadcastForm.Visibility}"/>
|
||
<CheckBox Content="Made for kids" Style="{StaticResource YtCheckBox}"
|
||
FontSize="12" Margin="0,2,0,10"
|
||
IsChecked="{Binding BroadcastForm.MadeForKids}"/>
|
||
<TextBlock Text="SCHEDULED START" Style="{StaticResource YtLabel}" Margin="0,0,0,3"/>
|
||
<TextBlock Text="{Binding BroadcastForm.ScheduledStartText}"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="12" Margin="0,0,0,4"/>
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
<StackPanel Grid.Row="2" Margin="0,10,0,0">
|
||
<Button Style="{StaticResource YtButton}" Content="Update Broadcast"
|
||
Command="{Binding BroadcastForm.UpdateBroadcastCommand}"/>
|
||
<TextBlock Text="{Binding BroadcastForm.UpdateError}"
|
||
Foreground="{StaticResource Brush.Error}" FontSize="11" TextWrapping="Wrap" Margin="0,6,0,0"
|
||
Visibility="{Binding BroadcastForm.UpdateError, Converter={StaticResource NotNullToVis}}"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
<!-- The Socials drawer (same pattern as the other pull-outs) -->
|
||
<Border Background="{StaticResource Brush.SurfaceContainer}" 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="{StaticResource Brush.OnSurfaceVariant}" 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="{StaticResource Brush.SurfaceContainer}" ClipToBounds="True">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Width" Value="0"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding Ypp.IsDrawerOpen}" 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="*"/>
|
||
<RowDefinition Height="Auto"/>
|
||
</Grid.RowDefinitions>
|
||
<Grid Grid.Row="0" Margin="0,0,0,8">
|
||
<TextBlock Text="YPP PROGRESS" Style="{StaticResource SectionHeader}"
|
||
HorizontalAlignment="Left"/>
|
||
<Button HorizontalAlignment="Right" VerticalAlignment="Top"
|
||
Background="Transparent" BorderThickness="0" Cursor="Hand"
|
||
Command="{Binding Ypp.CloseDrawerCommand}"
|
||
ToolTip="Close">
|
||
<Button.Template>
|
||
<ControlTemplate TargetType="Button">
|
||
<ContentPresenter/>
|
||
</ControlTemplate>
|
||
</Button.Template>
|
||
<TextBlock Text="✕" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="14" FontWeight="Bold"/>
|
||
</Button>
|
||
</Grid>
|
||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||
<StackPanel>
|
||
<TextBlock Text="Sign in to YouTube to see your Partner Program progress and eligibility checklist here."
|
||
Foreground="{StaticResource Brush.OnSurface}" FontSize="13" TextWrapping="Wrap"
|
||
Visibility="{Binding Ypp.IsSignedOut, Converter={StaticResource BoolToVis}}"/>
|
||
<TextBlock Text="Refreshing from YouTube…" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="12"
|
||
Margin="0,0,0,4"
|
||
Visibility="{Binding Ypp.IsLoading, Converter={StaticResource BoolToVis}}"/>
|
||
<TextBlock Text="{Binding Ypp.LoadError}" Foreground="{StaticResource Brush.Error}" FontSize="11"
|
||
TextWrapping="Wrap" Margin="0,0,0,4"
|
||
Visibility="{Binding Ypp.LoadError, Converter={StaticResource NotNullToVis}}"/>
|
||
<StackPanel Visibility="{Binding Ypp.IsSignedOut, Converter={StaticResource InverseBoolToVis}}">
|
||
<TextBlock Text="{Binding Ypp.DisplayName}" Foreground="{StaticResource Brush.OnSurface}" FontSize="13"
|
||
FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||
<TextBlock Text="SUBSCRIBERS — TIER 1 · FAN FUNDING" Style="{StaticResource YtLabel}" Margin="0,0,0,2"/>
|
||
<TextBlock Text="{Binding Ypp.Tier1SubsText}" Foreground="{StaticResource Brush.OnSurface}" FontSize="13"/>
|
||
<Border Height="8" Background="{StaticResource Brush.SurfaceContainerHigh}" CornerRadius="4" Margin="0,4,0,10">
|
||
<Border Height="8" Background="{StaticResource Brush.Tertiary}" CornerRadius="4" HorizontalAlignment="Left"
|
||
Width="{Binding Ypp.Tier1SubsBarWidth}"/>
|
||
</Border>
|
||
<TextBlock Text="SUBSCRIBERS — TIER 2 · AD REVENUE" Style="{StaticResource YtLabel}" Margin="0,0,0,2"/>
|
||
<TextBlock Text="{Binding Ypp.Tier2SubsText}" Foreground="{StaticResource Brush.OnSurface}" FontSize="13"/>
|
||
<Border Height="8" Background="{StaticResource Brush.SurfaceContainerHigh}" CornerRadius="4" Margin="0,4,0,10">
|
||
<Border Height="8" Background="{StaticResource Brush.Tertiary}" CornerRadius="4" HorizontalAlignment="Left"
|
||
Width="{Binding Ypp.Tier2SubsBarWidth}"/>
|
||
</Border>
|
||
<TextBlock Text="PUBLIC UPLOADS (90 DAYS)" Style="{StaticResource YtLabel}" Margin="0,0,0,2"/>
|
||
<TextBlock Text="{Binding Ypp.UploadsText}" Foreground="{StaticResource Brush.OnSurface}" FontSize="13"/>
|
||
<Border Height="8" Background="{StaticResource Brush.SurfaceContainerHigh}" CornerRadius="4" Margin="0,4,0,6">
|
||
<Border Height="8" Background="{StaticResource Brush.Tertiary}" CornerRadius="4" HorizontalAlignment="Left"
|
||
Width="{Binding Ypp.UploadsBarWidth}"/>
|
||
</Border>
|
||
<TextBlock Text="LONG-FORM WATCH HOURS + SHORTS VIEWS" Style="{StaticResource YtLabel}" Margin="0,10,0,2"/>
|
||
<TextBlock Text="These arrive with the extended analytics update — subscriber + upload + standing progress is live now."
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="12" TextWrapping="Wrap"/>
|
||
|
||
<TextBlock Text="COMPLIANCE CHECKLIST" Style="{StaticResource YtLabel}" Margin="0,14,0,4"/>
|
||
<TextBlock Text="CHANNEL STANDING" Style="{StaticResource YtLabel}" Margin="0,0,0,2"/>
|
||
<TextBlock Text="{Binding Ypp.StandingDetailText}" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
|
||
TextWrapping="Wrap" Margin="0,0,0,8"/>
|
||
<CheckBox Content="Two-Step Verification switched on" Style="{StaticResource YtCheckBox}"
|
||
FontSize="12" Margin="0,2,0,0"
|
||
IsChecked="{Binding Ypp.TwoFactorEnabled}"/>
|
||
<Button Background="Transparent" BorderThickness="0" Cursor="Hand" HorizontalAlignment="Left"
|
||
Command="{Binding Ypp.OpenTwoStepCommand}" ToolTip="Open Google's 2-Step Verification page">
|
||
<Button.Template>
|
||
<ControlTemplate TargetType="Button">
|
||
<ContentPresenter/>
|
||
</ControlTemplate>
|
||
</Button.Template>
|
||
<TextBlock Text="Set up on Google →" Foreground="{StaticResource Brush.Tertiary}" FontSize="12" Margin="0,0,0,6"/>
|
||
</Button>
|
||
<CheckBox Content="AdSense account linked" Style="{StaticResource YtCheckBox}"
|
||
FontSize="12" Margin="0,2,0,0"
|
||
IsChecked="{Binding Ypp.AdSenseLinked}"/>
|
||
<Button Background="Transparent" BorderThickness="0" Cursor="Hand" HorizontalAlignment="Left"
|
||
Command="{Binding Ypp.OpenEarnCommand}" ToolTip="Open the YouTube Earn page">
|
||
<Button.Template>
|
||
<ControlTemplate TargetType="Button">
|
||
<ContentPresenter/>
|
||
</ControlTemplate>
|
||
</Button.Template>
|
||
<TextBlock Text="How to link on YouTube →" Foreground="{StaticResource Brush.Tertiary}" FontSize="12" Margin="0,0,0,6"/>
|
||
</Button>
|
||
|
||
<TextBlock Text="WHAT COUNTS" Style="{StaticResource YtLabel}" Margin="0,10,0,4"/>
|
||
<TextBlock Text="{Binding Ypp.WhatCountsText}" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="12"
|
||
TextWrapping="Wrap" Margin="0,0,0,6"/>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
<StackPanel Grid.Row="2" Margin="0,10,0,0">
|
||
<Button Style="{StaticResource YtButton}" Content="Refresh from YouTube"
|
||
Command="{Binding Ypp.RefreshCommand}"/>
|
||
<TextBlock Text="{Binding Ypp.LastUpdatedText}" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,6,0,0"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
<!-- The Test-stream drawer (below YPP on the rail) -->
|
||
<Border Background="{StaticResource Brush.SurfaceContainer}" ClipToBounds="True">
|
||
<Border.Style>
|
||
<Style TargetType="Border">
|
||
<Setter Property="Width" Value="0"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding TestSession.IsDrawerOpen}" 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="*"/>
|
||
<RowDefinition Height="Auto"/>
|
||
</Grid.RowDefinitions>
|
||
<Grid Grid.Row="0" Margin="0,0,0,8">
|
||
<TextBlock Text="TEST STREAM" Style="{StaticResource SectionHeader}"
|
||
HorizontalAlignment="Left"/>
|
||
<Button HorizontalAlignment="Right" VerticalAlignment="Top"
|
||
Background="Transparent" BorderThickness="0" Cursor="Hand"
|
||
Command="{Binding TestSession.CloseDrawerCommand}"
|
||
ToolTip="Close">
|
||
<Button.Template>
|
||
<ControlTemplate TargetType="Button">
|
||
<ContentPresenter/>
|
||
</ControlTemplate>
|
||
</Button.Template>
|
||
<TextBlock Text="✕" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="14" FontWeight="Bold"/>
|
||
</Button>
|
||
</Grid>
|
||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||
<StackPanel>
|
||
<TextBlock Text="{Binding TestSession.SessionCaption}"
|
||
Foreground="{StaticResource Brush.OnSurface}" FontSize="13" TextWrapping="Wrap" Margin="0,0,0,8"/>
|
||
<TextBlock Text="{Binding TestSession.ChatIdLine}"
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
|
||
Visibility="{Binding TestSession.IsTestSession, Converter={StaticResource BoolToVis}}"/>
|
||
|
||
<TextBlock Text="MOCK CHAT INPUT" Style="{StaticResource YtLabel}" Margin="0,10,0,3"/>
|
||
<TextBlock Text="Sends a real live chat message — it round-trips through the actual chat poll and renders on your chat overlay."
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" TextWrapping="Wrap" Margin="0,0,0,4"/>
|
||
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,6"
|
||
Text="{Binding TestSession.SendMessageText, UpdateSourceTrigger=PropertyChanged}"/>
|
||
<Button Style="{StaticResource YtButton}" Content="Send Message"
|
||
Command="{Binding TestSession.SendTestMessageCommand}"/>
|
||
|
||
<TextBlock Text="SIMULATED EVENTS" Style="{StaticResource YtLabel}" Margin="0,14,0,3"/>
|
||
<TextBlock Text="Fired into your chat overlay locally — YouTube's insert API only creates text, so these never reach YouTube."
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" TextWrapping="Wrap" Margin="0,0,0,4"/>
|
||
<StackPanel Orientation="Horizontal" Margin="0,0,0,4">
|
||
<Button Style="{StaticResource YtButtonSecondary}" Content="Subscriber" Padding="10,4" FontSize="11"
|
||
Margin="0,0,6,0" Command="{Binding TestSession.SimulateSubscriberCommand}"/>
|
||
<Button Style="{StaticResource YtButtonSecondary}" Content="New Member" Padding="10,4" FontSize="11"
|
||
Margin="0,0,6,0" Command="{Binding TestSession.SimulateMemberCommand}"/>
|
||
</StackPanel>
|
||
<Button Style="{StaticResource YtButtonSecondary}" Content="Super Chat (5.00)" Padding="10,4" FontSize="11"
|
||
HorizontalAlignment="Left" Command="{Binding TestSession.SimulateSuperChatCommand}"/>
|
||
|
||
<TextBlock Text="{Binding TestSession.LastEventText}"
|
||
Foreground="{StaticResource Brush.Positive}" FontSize="11" TextWrapping="Wrap" Margin="0,10,0,0"
|
||
Visibility="{Binding TestSession.LastEventText, Converter={StaticResource NotNullToVis}}"/>
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
<StackPanel Grid.Row="2" Margin="0,10,0,0">
|
||
<Button Style="{StaticResource YtButton}" Content="End Test"
|
||
Command="{Binding TestSession.EndTestCommand}"
|
||
Visibility="{Binding TestSession.IsTestSession, Converter={StaticResource BoolToVis}}"/>
|
||
<TextBlock Text="Test broadcasts are private — nobody is notified, and it runs through the exact same pipeline as a real live."
|
||
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" TextWrapping="Wrap" Margin="0,6,0,0"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
<!-- The stacked white tabs -->
|
||
<StackPanel Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Stretch">
|
||
<Border Width="30" VerticalAlignment="Center" Background="{StaticResource Brush.Attention}"
|
||
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>
|
||
<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="{StaticResource Brush.Primary}" 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="{StaticResource Brush.Attention}"
|
||
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>
|
||
<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="{StaticResource Brush.Primary}" 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="{StaticResource Brush.Tertiary}"
|
||
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="{StaticResource Brush.Background}" 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="{StaticResource Brush.Background}" FontWeight="Bold" FontSize="15"
|
||
HorizontalAlignment="Center">
|
||
<TextBlock.LayoutTransform>
|
||
<RotateTransform Angle="90"/>
|
||
</TextBlock.LayoutTransform>
|
||
</TextBlock>
|
||
</StackPanel>
|
||
</Border>
|
||
<Border Width="30" VerticalAlignment="Center" Background="{StaticResource Brush.Attention}"
|
||
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>
|
||
<StackPanel HorizontalAlignment="Center">
|
||
<Path Data="M8,2 H16 M9,2 V8 L4,18 H20 L15,8 V2 M7,14 H17"
|
||
Stroke="{StaticResource Brush.Background}" 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="{StaticResource Brush.Background}" FontWeight="Bold" FontSize="15"
|
||
HorizontalAlignment="Center">
|
||
<TextBlock.LayoutTransform>
|
||
<RotateTransform Angle="90"/>
|
||
</TextBlock.LayoutTransform>
|
||
</TextBlock>
|
||
</StackPanel>
|
||
</Border>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
</Grid>
|
||
|
||
<!-- ═══ LIVE CONTROLS (below preview, never overlaps) ═══
|
||
Line 1: Socials + TRAX meter (desktop/game audio)
|
||
Line 2: Mic meter + volume -->
|
||
<StackPanel Grid.Row="2" Orientation="Vertical" HorizontalAlignment="Center"
|
||
VerticalAlignment="Center" Margin="0,4">
|
||
<!-- Line 1: Socials + TRAX -->
|
||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,4">
|
||
<!-- TRAX meter -->
|
||
<Border Background="{StaticResource Brush.ScrimDarkStrong}" CornerRadius="6" Padding="12,8">
|
||
<StackPanel Orientation="Horizontal">
|
||
<Button Style="{StaticResource YtButtonSecondary}" Padding="8,2"
|
||
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0"
|
||
ToolTipService.ToolTip="{Binding TraxToolTip}"
|
||
ToolTipService.Placement="Top"
|
||
Click="TraxButton_Click"
|
||
PreviewMouseRightButtonUp="TraxButton_PreviewMouseRightButtonUp">
|
||
<StackPanel Orientation="Horizontal">
|
||
<Ellipse Width="8" Height="8" Fill="{Binding TraxDotBrush}"
|
||
Margin="0,0,6,0" VerticalAlignment="Center"/>
|
||
<TextBlock Text="TRAX"/>
|
||
</StackPanel>
|
||
</Button>
|
||
<TextBlock Text="Desktop Audio" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
|
||
FontWeight="SemiBold" VerticalAlignment="Center" Margin="0,0,8,0"
|
||
ToolTip="Desktop/game audio via WASAPI loopback (automatic); TRAX music rides the same channel"/>
|
||
<Border Width="288" Height="14" CornerRadius="7" Background="{StaticResource Brush.SurfaceContainerHighest}" Margin="0,0,8,0"
|
||
ClipToBounds="True" VerticalAlignment="Center">
|
||
<Grid>
|
||
<Rectangle Width="57" Fill="{StaticResource Brush.SecondaryContainer}" HorizontalAlignment="Left" Margin="173,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
<Rectangle Width="58" Fill="{StaticResource Brush.ErrorContainer}" HorizontalAlignment="Left" Margin="230,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
<Border HorizontalAlignment="Left" Width="{Binding GameMeterFillWidth}"
|
||
Background="{Binding GameMeterBrush}" Opacity="0.75"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="36,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="72,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="108,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="144,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="180,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="216,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="252,0,0,0"/>
|
||
<Rectangle Width="2" Fill="{StaticResource Brush.ScrimDarkStrong}" HorizontalAlignment="Left" Margin="173,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
<Rectangle Width="2" Fill="{StaticResource Brush.ScrimDarkStrong}" HorizontalAlignment="Left" Margin="230,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
<!-- TRAX error: a terse message that scrolls across the bar
|
||
(marquee) while the TRAX dot is red. -->
|
||
<Canvas x:Name="TraxErrorMarquee" ClipToBounds="True"
|
||
Visibility="{Binding TraxError, Converter={StaticResource NotNullToVis}}">
|
||
<TextBlock x:Name="TraxErrorText" Text="{Binding TraxError}" Foreground="{StaticResource Brush.Error}" FontSize="10"
|
||
FontWeight="SemiBold" Canvas.Top="0">
|
||
<TextBlock.RenderTransform>
|
||
<TranslateTransform x:Name="TraxErrorScroll"/>
|
||
</TextBlock.RenderTransform>
|
||
<TextBlock.Triggers>
|
||
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
|
||
<BeginStoryboard>
|
||
<Storyboard RepeatBehavior="Forever">
|
||
<DoubleAnimation Storyboard.TargetName="TraxErrorScroll"
|
||
Storyboard.TargetProperty="X"
|
||
From="288" To="-220" Duration="0:0:7"/>
|
||
</Storyboard>
|
||
</BeginStoryboard>
|
||
</EventTrigger>
|
||
</TextBlock.Triggers>
|
||
</TextBlock>
|
||
</Canvas>
|
||
</Grid>
|
||
</Border>
|
||
<Grid Width="16" Height="16" Cursor="Hand" Margin="0,0,8,0"
|
||
VerticalAlignment="Center" ToolTip="{Binding GameMuteText}"
|
||
MouseLeftButtonUp="GameSpeaker_MouseLeftButtonUp">
|
||
<Path Fill="{StaticResource Brush.OnSurface}" Stretch="Uniform"
|
||
Data="M3,9v6h4l5,5V4L7,9H3zM16.5,12c0,-1.77 -1,-3.29 -2.5,-4.03v8.05c1.5,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"
|
||
Visibility="{Binding GameMuted, Converter={StaticResource InverseBoolToVis}}"/>
|
||
<Path Fill="{StaticResource Brush.Error}" Stretch="Uniform"
|
||
Data="M3,9v6h4l5,5V4L7,9H3zM16.5,12c0,-1.77 -1,-3.29 -2.5,-4.03v8.05c1.5,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"
|
||
Visibility="{Binding GameMuted, Converter={StaticResource BoolToVis}}"/>
|
||
<Path Stroke="{StaticResource Brush.Error}" StrokeThickness="2.5" StrokeEndLineCap="Round" Stretch="Uniform"
|
||
Data="M21,3 L3,21"
|
||
Visibility="{Binding GameMuted, Converter={StaticResource BoolToVis}}"/>
|
||
</Grid>
|
||
<Slider Width="130" Minimum="0" Maximum="1"
|
||
Value="{Binding GameAudioVolume, Mode=TwoWay}" VerticalAlignment="Center"
|
||
PreviewMouseLeftButtonDown="GameVolumeSlider_PreviewMouseLeftButtonDown"
|
||
PreviewMouseLeftButtonUp="GameVolumeSlider_PreviewMouseLeftButtonUp"
|
||
LostMouseCapture="GameVolumeSlider_LostMouseCapture"/>
|
||
</StackPanel>
|
||
</Border>
|
||
</StackPanel>
|
||
<!-- Line 2: Mic meter + volume -->
|
||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||
<Button Command="{Binding OpenMicPickerCommand}"
|
||
Style="{StaticResource YtButtonSecondary}" Padding="12,4"
|
||
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0"
|
||
ToolTip="{Binding MicStatusToolTip}">
|
||
<StackPanel Orientation="Horizontal">
|
||
<Ellipse Width="8" Height="8" Fill="{Binding MicStatusBrush}"
|
||
Margin="0,0,6,0" VerticalAlignment="Center"/>
|
||
<TextBlock Text="MIC"/>
|
||
</StackPanel>
|
||
</Button>
|
||
<Border Width="288" Height="14" CornerRadius="7" Background="{StaticResource Brush.SurfaceContainerHighest}" Margin="0,0,8,0"
|
||
ClipToBounds="True" VerticalAlignment="Center">
|
||
<Grid>
|
||
<Rectangle Width="57" Fill="{StaticResource Brush.SecondaryContainer}" HorizontalAlignment="Left" Margin="173,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
<Rectangle Width="58" Fill="{StaticResource Brush.ErrorContainer}" HorizontalAlignment="Left" Margin="230,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
<TextBlock Text="{Binding MicSourceName}" Foreground="{StaticResource Brush.OnSurface}" FontSize="10"
|
||
VerticalAlignment="Center" Margin="6,0,0,0" MaxWidth="270"
|
||
TextTrimming="CharacterEllipsis"
|
||
Visibility="{Binding MicSourceName, Converter={StaticResource NotNullToVis}}"/>
|
||
<Border HorizontalAlignment="Left" Width="{Binding MeterFillWidth}"
|
||
Background="{Binding MeterBrush}" Opacity="0.75"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="36,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="72,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="108,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="144,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="180,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="216,0,0,0"/>
|
||
<Rectangle Width="1" Height="10" Fill="{StaticResource Brush.ScrimDark}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="252,0,0,0"/>
|
||
<Rectangle Width="2" Fill="{StaticResource Brush.ScrimDarkStrong}" HorizontalAlignment="Left" Margin="173,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
<Rectangle Width="2" Fill="{StaticResource Brush.ScrimDarkStrong}" HorizontalAlignment="Left" Margin="230,0,0,0"
|
||
VerticalAlignment="Stretch"/>
|
||
</Grid>
|
||
</Border>
|
||
<Grid Width="16" Height="16" Cursor="Hand" Margin="0,0,8,0"
|
||
VerticalAlignment="Center" ToolTip="{Binding MicMuteText}"
|
||
MouseLeftButtonUp="MicSpeaker_MouseLeftButtonUp">
|
||
<Path Fill="{StaticResource Brush.OnSurface}" Stretch="Uniform"
|
||
Data="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.49 6,-3.31 6,-6.72H17.3z"
|
||
Visibility="{Binding MicMuted, Converter={StaticResource InverseBoolToVis}}"/>
|
||
<Path Fill="{StaticResource Brush.Error}" Stretch="Uniform"
|
||
Data="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.49 6,-3.31 6,-6.72H17.3z"
|
||
Visibility="{Binding MicMuted, Converter={StaticResource BoolToVis}}"/>
|
||
<Path Stroke="{StaticResource Brush.Error}" StrokeThickness="2.5" StrokeEndLineCap="Round" Stretch="Uniform"
|
||
Data="M21,3 L3,21"
|
||
Visibility="{Binding MicMuted, Converter={StaticResource BoolToVis}}"/>
|
||
</Grid>
|
||
<Slider Width="130" Minimum="0" Maximum="1"
|
||
Value="{Binding MicVolume, Mode=TwoWay}" VerticalAlignment="Center"
|
||
PreviewMouseLeftButtonDown="VolumeSlider_PreviewMouseLeftButtonDown"
|
||
PreviewMouseLeftButtonUp="VolumeSlider_PreviewMouseLeftButtonUp"
|
||
LostMouseCapture="VolumeSlider_LostMouseCapture"/>
|
||
<StackPanel Orientation="Horizontal" Margin="10,0,0,0" VerticalAlignment="Center"
|
||
ToolTip="Audio sync (−500..+500 ms). Positive: delays the audio when it runs ahead of the video. Negative: advances it (drops the first |N| ms of the recording) when it runs behind. Calibrate with a clap: is the clap late in the video, move negative; early, move positive.">
|
||
<TextBlock Text="AUDIO SYNC" FontSize="9" Foreground="{StaticResource Brush.OnSurfaceVariant}"
|
||
VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||
<Slider Width="70" Minimum="-500" Maximum="500"
|
||
Value="{Binding AudioSyncOffsetMs, Mode=TwoWay}"
|
||
IsEnabled="{Binding IsEditMode}"
|
||
VerticalAlignment="Center"
|
||
ToolTip="{Binding AudioSyncOffsetMs, StringFormat=Audio sync: {0} ms}"/>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
</UserControl>
|