Control Surface UX: pill toggle, background rename, per-scene properties
- Sliding pill toggle (green ON / red OFF) replaces checkbox - Geometry mismatch error for custom backgrounds - Backdrop -> Background: C# rename, SQL column rename with migration - Source.IsBackground: new -> override (fixes WPF binding) - Legacy 'Backdrop' display name migrated to 'Background' - Per-scene UseDefaultBackground + CustomBackgroundPath - Asset files renamed backdrop -> background - StagedScene/LiveScene split, thumbnail strip, edit gating - TransitionService (Cut/Fade/Move) - Context menu: Capture Desktop, Refresh Desktop - Resolution badge removed, ON-AIR status light added
This commit is contained in:
+218
-254
@@ -16,7 +16,8 @@
|
||||
Loaded="MainWindow_Loaded"
|
||||
Activated="MainWindow_Activated"
|
||||
Deactivated="MainWindow_Deactivated"
|
||||
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown">
|
||||
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
|
||||
ContextMenuOpening="Window_ContextMenuOpening">
|
||||
|
||||
<Window.DataContext>
|
||||
<vm:MainViewModel/>
|
||||
@@ -95,19 +96,28 @@
|
||||
|
||||
<!-- Logo / Brand — clicking opens the in-app About overlay -->
|
||||
<Button Grid.Column="0" Background="Transparent" BorderThickness="0" Cursor="Hand"
|
||||
VerticalAlignment="Center" Margin="0,0,24,0" ToolTip="About ytLlive"
|
||||
VerticalAlignment="Center" Margin="0,0,24,0" ToolTip="About LlamaCasty"
|
||||
Command="{Binding OpenAboutCommand}">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<ContentPresenter/>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
<TextBlock Text="ytLlive" Foreground="#e94560" FontSize="22" FontWeight="Bold"/>
|
||||
<TextBlock FontSize="22" FontWeight="Bold">
|
||||
<Run Text="LlamaCas" Foreground="#e0e0e0"/><Run Text="ty" Foreground="#e94560"/>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
|
||||
<!-- REC sign (center): dark offline, red live, darker red when private -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<!-- ON-AIR indicator -->
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,16,0"
|
||||
Visibility="{Binding IsLive, Converter={StaticResource BoolToVis}}">
|
||||
<Ellipse Width="12" Height="12" Fill="#e94560" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="ON-AIR" FontSize="16" FontWeight="Bold"
|
||||
Foreground="#e94560" VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
<Ellipse Width="12" Height="12" VerticalAlignment="Center"
|
||||
Fill="{Binding RecDotBrush}" Opacity="{Binding RecDotOpacity}"/>
|
||||
<TextBlock Text="REC" FontSize="16" FontWeight="Bold"
|
||||
@@ -174,93 +184,96 @@
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="2" Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" MinWidth="220" MaxWidth="360"/>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="1*" MinWidth="300" MaxWidth="400"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- LEFT PANEL: Scenes & Sources -->
|
||||
<Border Grid.Column="0" Background="#16213e" CornerRadius="6" Padding="12">
|
||||
<!-- ═══ THUMBNAIL STRIP ═══ -->
|
||||
<Border Grid.Row="0" Grid.ColumnSpan="2" Background="#0d1117" CornerRadius="4" Margin="0,0,0,4"
|
||||
Padding="4">
|
||||
<ItemsControl ItemsSource="{Binding Scenes}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Rows="1" Columns="5"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border CornerRadius="4" Margin="2" Padding="2"
|
||||
BorderThickness="2"
|
||||
BorderBrush="Transparent"
|
||||
Cursor="Hand"
|
||||
MouseLeftButtonDown="Thumbnail_MouseLeftButtonDown">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="BorderBrush" Value="#333344"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsStaged}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="#e94560"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsLive}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="#22c55e"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" CornerRadius="3"
|
||||
Background="#0a0a1a" ClipToBounds="True">
|
||||
<Image Source="{Binding Snapshot}"
|
||||
Stretch="UniformToFill"
|
||||
RenderOptions.BitmapScalingMode="LowQuality"/>
|
||||
</Border>
|
||||
<TextBlock Grid.Row="1" Text="{Binding Name}"
|
||||
Foreground="#a0a0b0" FontSize="10"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,2,0,0"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
|
||||
<!-- LEFT PANEL: one panel, two states.
|
||||
Offline (edit mode): LAYERS + PROPERTIES
|
||||
Live/Recording: YouTube Live Chat -->
|
||||
<Border Grid.Row="1" Grid.Column="0" Background="#16213e" CornerRadius="6" Padding="12">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Scenes: the app has five canonical scenes. Work with less,
|
||||
never more — the "+" only offers the missing ones, and is
|
||||
hidden entirely once all five are back. -->
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<TextBlock Text="SCREENS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
||||
<Button ToolTip="Add Missing Screen" Click="AddSceneButton_Click"
|
||||
Style="{StaticResource IconButton}" Margin="4,-4,0,0"
|
||||
Visibility="{Binding ShowAddScene, Converter={StaticResource BoolToVis}}">
|
||||
<Button.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"
|
||||
ItemsSource="{Binding MissingScenes}">
|
||||
<ContextMenu.ItemContainerStyle>
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Header" Value="{Binding}"/>
|
||||
<Setter Property="Command"
|
||||
Value="{Binding DataContext.AddSceneCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding}"/>
|
||||
</Style>
|
||||
</ContextMenu.ItemContainerStyle>
|
||||
</ContextMenu>
|
||||
</Button.ContextMenu>
|
||||
<Path Data="M12,4 L12,20 M4,12 L20,12" Stroke="#e94560" StrokeThickness="2"
|
||||
Width="14" Height="14" Stretch="Uniform"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<!-- Edit mode (offline): layers + properties -->
|
||||
<Grid Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BoolToVis}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Helpers:FocusPreservingListBox x:Name="SceneList" Grid.Row="1" Background="Transparent" BorderThickness="0"
|
||||
ItemsSource="{Binding Scenes}"
|
||||
SelectedItem="{Binding ActiveScene}"
|
||||
SelectionChanged="SceneList_SelectionChanged"
|
||||
PreviewMouseLeftButtonDown="List_PreviewMouseLeftButtonDown"
|
||||
PreviewMouseMove="List_PreviewMouseMove"
|
||||
PreviewMouseLeftButtonUp="List_PreviewMouseLeftButtonUp"
|
||||
Foreground="#e0e0e0" FontSize="13">
|
||||
<Helpers:FocusPreservingListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem" BasedOn="{StaticResource YtListBoxItem}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHidden}" Value="True">
|
||||
<Setter Property="Opacity" Value="0.45"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Helpers:FocusPreservingListBox.ItemContainerStyle>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,2">
|
||||
<TextBlock Text="{Binding Name, Converter={x:Static models:SceneCatalog.DisplayNameConverter}}" VerticalAlignment="Center"
|
||||
Padding="4,2" TextTrimming="CharacterEllipsis"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</Helpers:FocusPreservingListBox>
|
||||
|
||||
<!-- Visual separation between the two listboxes: a hairline
|
||||
with breathing room on either side (the creator's fix). -->
|
||||
<Rectangle Grid.Row="2" Height="1" Fill="#2c3a5c" Margin="0,10,0,2"/>
|
||||
|
||||
<!-- Sources (for active scene) -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="0,8,0,0">
|
||||
<!-- LAYERS header + Add button -->
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,8,0,0">
|
||||
<TextBlock Text="LAYERS" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
||||
<Button ToolTip="Add Layer" Style="{StaticResource IconButton}" Margin="4,-4,0,0"
|
||||
Click="AddSourceButton_Click">
|
||||
<Button.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Webcam" Command="{Binding AddWebcamCommand}"
|
||||
IsEnabled="{Binding CanAddWebcamToActiveScene}"
|
||||
IsEnabled="{Binding CanAddWebcamToStagedScene}"
|
||||
ToolTip="One webcam at a time — it's already in your stream"/>
|
||||
<MenuItem Header="Screen" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.DisplayCapture}"/>
|
||||
<MenuItem Header="Background" Command="{Binding AddSourceCommand}" CommandParameter="{x:Static models:SourceType.Background}"/>
|
||||
@@ -280,8 +293,8 @@
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<Helpers:FocusPreservingListBox x:Name="SourceList" Grid.Row="4" Background="Transparent" BorderThickness="0"
|
||||
ItemsSource="{Binding ActiveScene.Elements}"
|
||||
<Helpers:FocusPreservingListBox x:Name="SourceList" Grid.Row="1" Background="Transparent" BorderThickness="0"
|
||||
ItemsSource="{Binding StagedScene.Elements}"
|
||||
SelectedItem="{Binding SelectedElement, Mode=TwoWay}"
|
||||
PreviewMouseLeftButtonDown="List_PreviewMouseLeftButtonDown"
|
||||
PreviewMouseMove="List_PreviewMouseMove"
|
||||
@@ -309,13 +322,13 @@
|
||||
|
||||
<Grid.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Change Capture…" Click="BackdropMenu_ChangeCapture"
|
||||
Visibility="{Binding IsBackdrop, Converter={StaticResource BoolToVis}}"/>
|
||||
<MenuItem Header="Refresh Capture" Click="BackdropMenu_RefreshCapture"
|
||||
Visibility="{Binding IsBackdrop, Converter={StaticResource BoolToVis}}"/>
|
||||
<Separator Visibility="{Binding IsBackdrop, Converter={StaticResource BoolToVis}}"/>
|
||||
<MenuItem Header="Change Capture…" Click="BackgroundMenu_ChangeCapture"
|
||||
Visibility="{Binding IsBackground, Converter={StaticResource BoolToVis}}"/>
|
||||
<MenuItem Header="Refresh Capture" Click="BackgroundMenu_RefreshCapture"
|
||||
Visibility="{Binding IsBackground, Converter={StaticResource BoolToVis}}"/>
|
||||
<Separator Visibility="{Binding IsBackground, Converter={StaticResource BoolToVis}}"/>
|
||||
<MenuItem Header="Remove Layer" Click="SourceMenu_Remove"
|
||||
Visibility="{Binding IsBackdrop, Converter={StaticResource InverseBoolToVis}}"/>
|
||||
Visibility="{Binding IsBackground, Converter={StaticResource InverseBoolToVis}}"/>
|
||||
</ContextMenu>
|
||||
</Grid.ContextMenu>
|
||||
|
||||
@@ -367,7 +380,7 @@
|
||||
<Button Grid.Column="3" ToolTip="Remove Layer"
|
||||
Command="{Binding DataContext.RemoveSourceCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||
CommandParameter="{Binding}" Style="{StaticResource IconButton}"
|
||||
Visibility="{Binding IsBackdrop, Converter={StaticResource HiddenBoolToVis}}">
|
||||
Visibility="{Binding IsBackground, 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"
|
||||
Fill="#d0d0d0" Width="13" Height="13" Stretch="Uniform"/>
|
||||
</Button>
|
||||
@@ -376,19 +389,19 @@
|
||||
</ListBox.ItemTemplate>
|
||||
</Helpers:FocusPreservingListBox>
|
||||
|
||||
<TextBlock Grid.Row="4" Text="No layers yet" Foreground="#555" FontSize="12"
|
||||
<TextBlock Grid.Row="2" Text="No layers yet" Foreground="#555" FontSize="12"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
|
||||
|
||||
<!-- ELEMENTS panel: per-element config beneath Sources.
|
||||
Collapses entirely when no element is selected. -->
|
||||
<Rectangle Grid.Row="5" Height="1" Fill="#2c3a5c" Margin="0,10,0,2"
|
||||
<Rectangle Grid.Row="3" 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"
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="0,8,0,0"
|
||||
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
||||
<TextBlock Text="PROPERTIES" Style="{StaticResource SectionHeader}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="7" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="5" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
||||
<StackPanel x:Name="ElementsPanel" Margin="0,4,0,0">
|
||||
<!-- Opacity + mirror/shape (draggable elements: image, webcam) -->
|
||||
@@ -592,29 +605,109 @@
|
||||
IsChecked="{Binding SelectedElement.ChatShowBadges, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Placeholder hint when no config available -->
|
||||
<TextBlock Text="No configurable properties" Foreground="#555" FontSize="11"
|
||||
HorizontalAlignment="Center" Margin="0,8,0,0"
|
||||
Visibility="{Binding IsElementsPanelEmpty, Converter={StaticResource BoolToVis}}"/>
|
||||
<!-- Background properties -->
|
||||
<StackPanel Margin="0,8,0,0"
|
||||
Visibility="{Binding SelectedElement.IsBackground, Converter={StaticResource BoolToVis}}">
|
||||
<TextBlock Text="BACKGROUND" Style="{StaticResource SectionHeader}"/>
|
||||
<ToggleButton Style="{StaticResource PillToggle}"
|
||||
IsChecked="{Binding UseDefaultBackground, Mode=TwoWay}"
|
||||
Margin="0,0,0,8"/>
|
||||
<Grid Margin="0,0,0,4"
|
||||
Visibility="{Binding UseDefaultBackground, Converter={StaticResource InverseBoolToVis}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.Column="0" Text="{Binding CustomBackgroundPath, Mode=OneWay}"
|
||||
IsReadOnly="True" Background="#0f3460" Foreground="#a0a0b0"
|
||||
FontSize="11" VerticalAlignment="Center"/>
|
||||
<Button Grid.Column="1" Content="Browse…" Command="{Binding BrowseBackgroundCommand}"
|
||||
Style="{StaticResource YtButtonSecondary}" Margin="4,0,0,0" Padding="8,2"/>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding BackgroundSizeMismatchText}"
|
||||
Foreground="#ef4444" FontSize="11" TextWrapping="Wrap"
|
||||
Margin="0,4,0,0"
|
||||
Visibility="{Binding BackgroundSizeMismatch, Converter={StaticResource BoolToVis}}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- CENTER: Preview Area -->
|
||||
<Border Grid.Column="1" Background="#0a0a1a" CornerRadius="6" Margin="8,0"
|
||||
BorderBrush="{Binding PreviewGlowBrush}" BorderThickness="{Binding PreviewGlowThickness}">
|
||||
<Grid>
|
||||
<!-- Live mode: YouTube Live Chat (entire left panel replaced) -->
|
||||
<Grid Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBoolToVis}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="{Binding ActiveScene.Name}"
|
||||
Foreground="#e0e0e0" FontSize="15" FontWeight="SemiBold"
|
||||
Margin="14,12,14,0"/>
|
||||
<TextBlock Grid.Row="0" Text="LIVE CHAT" Style="{StaticResource SectionHeader}"/>
|
||||
|
||||
<Grid x:Name="PreviewGrid" Grid.Row="1"
|
||||
<!-- Inactive overlay (shown when NOT live) -->
|
||||
<StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource BoolToVis}}">
|
||||
<TextBlock Text="Chat is inactive" Foreground="#a0a0b0" FontSize="13"
|
||||
HorizontalAlignment="Center" Margin="0,0,0,4"/>
|
||||
<TextBlock Text="Chat becomes active once LlamaCasty is connected and streaming on YouTube."
|
||||
Foreground="#666" FontSize="11" TextWrapping="Wrap"
|
||||
HorizontalAlignment="Center" TextAlignment="Center" MaxWidth="240"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Message feed (shown when live) -->
|
||||
<ListBox Grid.Row="1" Background="Transparent" BorderThickness="0"
|
||||
ItemsSource="{Binding ChatMessages}"
|
||||
ItemContainerStyle="{StaticResource YtListBoxItem}"
|
||||
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource InverseBoolToVis}}"
|
||||
x:Name="ChatListBox">
|
||||
<ListBox.ItemTemplate><DataTemplate>
|
||||
<Border x:Name="MessageBorder" Padding="6,4" CornerRadius="4">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSuperChat}" Value="True">
|
||||
<Setter Property="Background" Value="#2a1a0a"/>
|
||||
<Setter Property="BorderBrush" Value="#eab308"/>
|
||||
<Setter Property="BorderThickness" Value="1,0,1,0"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,2">
|
||||
<TextBlock Text="⭐" FontSize="10" Margin="0,0,4,0"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{Binding IsMember, Converter={StaticResource BoolToVis}}"/>
|
||||
<TextBlock Text="{Binding AuthorName}" Foreground="#e94560"
|
||||
FontWeight="Bold" FontSize="12" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding SuperChatAmount, StringFormat=${0:F2}}"
|
||||
Foreground="#eab308" FontWeight="Bold" FontSize="12"
|
||||
Margin="8,0,0,0" VerticalAlignment="Center"
|
||||
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
|
||||
<TextBlock Text="{Binding SuperChatCurrency}" Foreground="#eab308"
|
||||
FontSize="11" Margin="2,0,0,0" VerticalAlignment="Center"
|
||||
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding Message}" Foreground="#e0e0e0"
|
||||
TextWrapping="Wrap" Margin="0,2,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate></ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- CENTER: Preview Area -->
|
||||
<Border Grid.Row="1" Grid.Column="1" Background="#0a0a1a" 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">
|
||||
@@ -622,40 +715,36 @@
|
||||
<Grid x:Name="CanvasGrid" Width="1920" Height="1080" ClipToBounds="True" Background="Transparent">
|
||||
<Grid.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Show Webcam" Command="{Binding ShowWebcamCommand}"/>
|
||||
<MenuItem Header="Show Desktop" IsCheckable="True"
|
||||
IsChecked="{Binding BackdropShowDesktop}"
|
||||
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"
|
||||
IsChecked="{Binding BackgroundShowDesktop}"
|
||||
Visibility="{Binding CanChangeBackground, Converter={StaticResource BoolToVis}}"
|
||||
ToolTip="Show desktop when no fullscreen game is detected"/>
|
||||
<MenuItem Header="Capture Display"
|
||||
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"
|
||||
<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.SetBackdropDisplayCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
||||
Value="{Binding DataContext.SetBackgroundDisplayCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding}"/>
|
||||
</Style>
|
||||
</MenuItem.ItemContainerStyle>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Change Capture…" Command="{Binding ChangeCaptureCommand}"
|
||||
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"
|
||||
ToolTip="Pick any window with the OS picker"/>
|
||||
<MenuItem Header="Refresh Capture" Command="{Binding RefreshCaptureCommand}"
|
||||
Visibility="{Binding CanChangeBackdrop, Converter={StaticResource BoolToVis}}"/>
|
||||
<MenuItem Header="Refresh Desktop" Command="{Binding RefreshCaptureCommand}"
|
||||
Visibility="{Binding CanChangeBackground, Converter={StaticResource BoolToVis}}"/>
|
||||
</ContextMenu>
|
||||
</Grid.ContextMenu>
|
||||
<!-- Live desktop/game backdrop (bottom layer; the static
|
||||
background renders above it). -->
|
||||
<Image Source="{Binding BackdropImage}" Stretch="UniformToFill"
|
||||
Visibility="{Binding BackdropVisible, Converter={StaticResource BoolToVis}}"
|
||||
<!-- Live desktop/game background (bottom layer; the static
|
||||
image renders above it). -->
|
||||
<Image Source="{Binding BackgroundImage}" Stretch="UniformToFill"
|
||||
Visibility="{Binding BackgroundVisible, Converter={StaticResource BoolToVis}}"
|
||||
IsHitTestVisible="False"/>
|
||||
<Image Source="{Binding ActiveBackgroundImage}" Stretch="UniformToFill"
|
||||
IsHitTestVisible="False"/>
|
||||
<Rectangle Stroke="#22c55e" StrokeThickness="6" IsHitTestVisible="False"/>
|
||||
<Canvas x:Name="OverlayCanvas" Width="1920" Height="1080">
|
||||
<ItemsControl ItemsSource="{Binding ActiveScene.Elements}"
|
||||
<ItemsControl ItemsSource="{Binding StagedScene.Elements}"
|
||||
Width="1920" Height="1080">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
@@ -681,10 +770,10 @@
|
||||
<DataTrigger Binding="{Binding IsVisible}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
<!-- The backdrop is a fixed full-frame layer (rendered separately,
|
||||
<!-- 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 IsBackdrop}" Value="True">
|
||||
<DataTrigger Binding="{Binding IsBackground}" Value="True">
|
||||
<Setter Property="IsHitTestVisible" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
@@ -935,7 +1024,7 @@
|
||||
</Grid.Style>
|
||||
<Viewbox Stretch="Uniform" MaxWidth="1600" MaxHeight="800"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock Text="made with ytLlive!" FontWeight="Bold" Foreground="White"
|
||||
<TextBlock Text="made with LlamaCasty!" FontWeight="Bold" Foreground="White"
|
||||
FontSize="180" TextAlignment="Center"/>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
@@ -976,10 +1065,6 @@
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
<Border HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,8,8,0"
|
||||
Background="#CC16213e" CornerRadius="4" Padding="8,3" IsHitTestVisible="False">
|
||||
<TextBlock Text="{Binding ResolutionBadgeText}" Foreground="#e0e0e0" FontSize="12"/>
|
||||
</Border>
|
||||
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Margin="8,8,0,0"
|
||||
Background="#CCe94560" CornerRadius="4" Padding="8,3" IsHitTestVisible="False"
|
||||
Visibility="{Binding WebcamError, Converter={StaticResource NotNullToVis}}">
|
||||
@@ -1004,16 +1089,12 @@
|
||||
TextWrapping="Wrap" MaxWidth="440" TextAlignment="Center"
|
||||
HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Desktop/game audio bar: overlaid at the bottom of the
|
||||
preview, always visible. Meter + mute + volume control
|
||||
live here; desktop audio itself is automatic WASAPI
|
||||
loopback. Volume + mute are stream-honest AND scale the
|
||||
TRAX music in the headphones, so the creator hears the
|
||||
control work. -->
|
||||
<Border HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||||
Background="#D9000000" CornerRadius="6" Padding="12,8"
|
||||
Margin="0,0,0,12">
|
||||
<!-- ═══ TRAX METER (below preview, never overlaps) ═══ -->
|
||||
<Border Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Background="#D9000000" CornerRadius="6" Padding="12,8"
|
||||
Margin="0,4">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!-- TRAX sits left of the "Desktop Audio" label
|
||||
(the creator's pick): it rides the desktop
|
||||
@@ -1081,122 +1162,7 @@
|
||||
PreviewMouseLeftButtonUp="GameVolumeSlider_PreviewMouseLeftButtonUp"
|
||||
LostMouseCapture="GameVolumeSlider_LostMouseCapture"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- RIGHT PANEL: Chat -->
|
||||
<Border Grid.Column="2" Background="#16213e" CornerRadius="6" Padding="12">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="LIVE CHAT" Style="{StaticResource SectionHeader}"/>
|
||||
|
||||
<StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource BoolToVis}}">
|
||||
<TextBlock Text="Chat is inactive" Foreground="#e0e0e0" FontSize="18"
|
||||
FontWeight="SemiBold" HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="Chat becomes active once ytLlive is connected and streaming on YouTube."
|
||||
Foreground="#a0a0b0" FontSize="13" Margin="0,8,0,0"
|
||||
TextWrapping="Wrap" MaxWidth="260" TextAlignment="Center"
|
||||
HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<ListBox Grid.Row="1" Background="Transparent" BorderThickness="0"
|
||||
ItemsSource="{Binding ChatMessages}"
|
||||
ItemContainerStyle="{StaticResource YtListBoxItem}"
|
||||
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource InverseBoolToVis}}"
|
||||
Foreground="#e0e0e0" FontSize="12"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
x:Name="ChatListBox">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border x:Name="MessageBorder" Padding="6,4" CornerRadius="4" Margin="0,1">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSuperChat}" Value="True">
|
||||
<Setter Property="Background" Value="#2a1a0a"/>
|
||||
<Setter Property="BorderBrush" Value="#eab308"/>
|
||||
<Setter Property="BorderThickness" Value="1,0,1,0"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!-- Member badge -->
|
||||
<TextBlock Text="⭐" FontSize="10" Margin="0,0,3,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsMember}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<!-- Author name -->
|
||||
<TextBlock FontWeight="Bold" Foreground="#e94560"
|
||||
Text="{Binding AuthorName, Mode=OneWay}"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<!-- Super Chat amount -->
|
||||
<TextBlock Foreground="#eab308" FontWeight="Bold"
|
||||
FontSize="11" Margin="6,0,0,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSuperChat}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
<Run Text="💰 $"/>
|
||||
<Run Text="{Binding SuperChatAmount, Mode=OneWay, StringFormat=F2}"/>
|
||||
<Run Text="{Binding SuperChatCurrency, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
|
||||
<!-- Timestamp -->
|
||||
<TextBlock Foreground="#555" FontSize="10"
|
||||
Margin="6,0,0,0" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Timestamp}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
<TextBlock.Text>
|
||||
<Binding Path="Timestamp" StringFormat="{}{0:HH:mm}"/>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Message -->
|
||||
<TextBlock Text="{Binding Message, Mode=OneWay}"
|
||||
Foreground="#e0e0e0" TextWrapping="Wrap"
|
||||
Margin="0,2,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
@@ -1428,7 +1394,7 @@
|
||||
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
|
||||
Text="{Binding BugReportEmail, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<TextBlock Text="Opens a pre-filled email to the ytLlive team." Style="{StaticResource YtLabel}"/>
|
||||
<TextBlock Text="Opens a pre-filled email to the LlamaCasty team." Style="{StaticResource YtLabel}"/>
|
||||
<Button Content="Send Report" Command="{Binding SubmitBugCommand}"
|
||||
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
|
||||
</StackPanel>
|
||||
@@ -1446,7 +1412,7 @@
|
||||
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
|
||||
Text="{Binding FeatureRequestEmail, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<TextBlock Text="Opens a pre-filled email to the ytLlive team." Style="{StaticResource YtLabel}"/>
|
||||
<TextBlock Text="Opens a pre-filled email to the LlamaCasty team." Style="{StaticResource YtLabel}"/>
|
||||
<Button Content="Send Request" Command="{Binding SubmitFeatureCommand}"
|
||||
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
|
||||
</StackPanel>
|
||||
@@ -1457,16 +1423,14 @@
|
||||
|
||||
<!-- Hub view -->
|
||||
<StackPanel Visibility="{Binding IsHubVisible, Converter={StaticResource BoolToVis}}">
|
||||
<Image Source="/ytLive;component/Assets/llamacasty-logo.jpg" Height="100" Stretch="Uniform"
|
||||
<Image Source="/ytLive;component/Assets/llamacasty-logo.jpg" Height="125" Stretch="Uniform"
|
||||
HorizontalAlignment="Center" Margin="0,0,0,12"/>
|
||||
<TextBlock Text="ytLlive" FontSize="22" FontWeight="Bold" Foreground="#e94560"
|
||||
HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding AppVersionLabel}" Foreground="#a0a0b0" FontSize="12"
|
||||
HorizontalAlignment="Center" Margin="0,4,0,0"/>
|
||||
<TextBlock Text="Live streaming for YouTube — simple enough that even the most right-brained person can intuit it."
|
||||
Foreground="#a0a0b0" FontSize="13" TextWrapping="Wrap" MaxWidth="380"
|
||||
TextAlignment="Center" Margin="0,16,0,0"/>
|
||||
<TextBlock Text="Free forever — live streams carry a brief "made with ytLlive!" flash. A subscription removes it and adds alerts."
|
||||
<TextBlock Text="Free forever — live streams carry a brief "made with LlamaCasty!" flash. A subscription removes it and adds alerts."
|
||||
Foreground="#888" FontSize="12" TextWrapping="Wrap" MaxWidth="380"
|
||||
TextAlignment="Center" Margin="0,12,0,0"/>
|
||||
<TextBlock Text="Made by gramps · llama chile shop" Foreground="#a0a0b0" FontSize="12"
|
||||
|
||||
Reference in New Issue
Block a user