Adopts the design language from Dammyjay93/interface-design: warm Stone neutrals, accent orange (#EA580C), borders-only depth (no shadows), 8px spacing grid, all-caps section labels, mono typography for machine names and timecodes. Themes/StoneTheme.xaml is the single source of truth for design tokens (color brushes, typography styles, spacing) plus restyled control templates (Button, TextBox, ComboBox, ComboBoxItem, CheckBox, DataGrid + DataGridColumnHeader / DataGridRow / DataGridCell, ScrollBar). MainWindow consumes the tokens and is laid out with a header (title + status pill), section-headed Settings sidebar (Output Format / NDI Network / Display), card-wrapped participant list, and a mono status footer. Settings sidebar surfaces the new NDI group configuration (discovery + output) and a Hide-(Local) checkbox. The latter filters the user's own self-preview from the participants list at the MainViewModel layer (HideLocalSelf=true by default) so operators don't accidentally route their own preview as an ISO. Apply Changes round-trips both FrameProcessingSettings and NdiGroupSettings through the controller in one go.
366 lines
19 KiB
XML
366 lines
19 KiB
XML
<Window x:Class="TeamsISO.App.MainWindow"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:vm="clr-namespace:TeamsISO.App.ViewModels"
|
|
xmlns:conv="clr-namespace:TeamsISO.App.Converters"
|
|
Title="TeamsISO"
|
|
Height="760" Width="1180"
|
|
MinHeight="600" MinWidth="960"
|
|
Background="{DynamicResource Stone.Canvas}"
|
|
UseLayoutRounding="True"
|
|
TextOptions.TextFormattingMode="Ideal"
|
|
TextOptions.TextRenderingMode="ClearType">
|
|
|
|
<Window.Resources>
|
|
<conv:BoolToVisibilityConverter x:Key="BoolToVis"/>
|
|
<conv:EnumDescriptionConverter x:Key="EnumDesc"/>
|
|
</Window.Resources>
|
|
|
|
<DockPanel LastChildFill="True">
|
|
|
|
<!-- ════════ Alert banner (top) ════════ -->
|
|
<Border DockPanel.Dock="Top"
|
|
Background="{DynamicResource Status.Bad.Surface}"
|
|
BorderBrush="{DynamicResource Status.Bad}"
|
|
BorderThickness="0,0,0,1"
|
|
Padding="20,10"
|
|
Visibility="{Binding AlertBanner.IsVisible, Converter={StaticResource BoolToVis}}">
|
|
<DockPanel>
|
|
<Button DockPanel.Dock="Right"
|
|
Style="{StaticResource Button.Ghost}"
|
|
Content="Dismiss"
|
|
Command="{Binding AlertBanner.DismissCommand}"
|
|
Margin="12,0,0,0"/>
|
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
<Ellipse Width="8" Height="8"
|
|
Fill="{DynamicResource Status.Bad}"
|
|
VerticalAlignment="Center"
|
|
Margin="0,0,10,0"/>
|
|
<TextBlock Text="{Binding AlertBanner.Message}"
|
|
Style="{StaticResource Text.Body}"
|
|
FontWeight="SemiBold"
|
|
VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
</DockPanel>
|
|
</Border>
|
|
|
|
<!-- ════════ Header ════════ -->
|
|
<Border DockPanel.Dock="Top"
|
|
Background="{DynamicResource Stone.Canvas}"
|
|
BorderBrush="{DynamicResource Stone.Border}"
|
|
BorderThickness="0,0,0,1"
|
|
Padding="24,18">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
<TextBlock Text="TeamsISO"
|
|
Style="{StaticResource Text.Display}"
|
|
FontWeight="Bold"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock Text="Per-participant NDI ISO controller"
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Tertiary}"
|
|
Margin="14,0,0,0"
|
|
VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
|
|
<!-- Engine status pill, right-aligned -->
|
|
<Border Grid.Column="1"
|
|
Style="{StaticResource Pill.Online}"
|
|
VerticalAlignment="Center">
|
|
<StackPanel Orientation="Horizontal">
|
|
<Ellipse Width="7" Height="7"
|
|
Fill="{DynamicResource Status.Good}"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock Text="{Binding StatusText}"
|
|
Style="{StaticResource Text.Body}"
|
|
FontSize="12"
|
|
Foreground="{DynamicResource Text.Primary}"
|
|
Margin="8,0,0,0"
|
|
VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
</Border>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ════════ Footer / status bar ════════ -->
|
|
<Border DockPanel.Dock="Bottom"
|
|
Background="{DynamicResource Stone.Canvas}"
|
|
BorderBrush="{DynamicResource Stone.Border}"
|
|
BorderThickness="0,1,0,0"
|
|
Padding="24,10">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
<TextBlock Text="{Binding StatusText}"
|
|
Style="{StaticResource Text.Mono}"
|
|
Foreground="{DynamicResource Text.Tertiary}"
|
|
VerticalAlignment="Center"/>
|
|
<TextBlock Grid.Column="1"
|
|
Text="Wild Dragon LLC · 1.0.0-alpha"
|
|
Style="{StaticResource Text.Mono}"
|
|
Foreground="{DynamicResource Text.Tertiary}"
|
|
VerticalAlignment="Center"/>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ════════ Settings panel (right) ════════ -->
|
|
<Border DockPanel.Dock="Right"
|
|
Width="360"
|
|
Background="{DynamicResource Stone.Surface}"
|
|
BorderBrush="{DynamicResource Stone.Border}"
|
|
BorderThickness="1,0,0,0">
|
|
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="0">
|
|
<StackPanel Margin="24,24,24,32">
|
|
|
|
<TextBlock Text="Settings"
|
|
Style="{StaticResource Text.Heading}"
|
|
FontSize="16"
|
|
Margin="0,0,0,20"/>
|
|
|
|
<!-- · · Output Format · · -->
|
|
<TextBlock Text="OUTPUT FORMAT" Style="{StaticResource Text.Caption}"/>
|
|
|
|
<TextBlock Text="Target Framerate"
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
Margin="0,8,0,4"/>
|
|
<ComboBox ItemsSource="{Binding Settings.AvailableFramerates}"
|
|
SelectedItem="{Binding Settings.Framerate}">
|
|
<ComboBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
|
</DataTemplate>
|
|
</ComboBox.ItemTemplate>
|
|
</ComboBox>
|
|
|
|
<TextBlock Text="Target Resolution"
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
Margin="0,12,0,4"/>
|
|
<ComboBox ItemsSource="{Binding Settings.AvailableResolutions}"
|
|
SelectedItem="{Binding Settings.Resolution}">
|
|
<ComboBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
|
</DataTemplate>
|
|
</ComboBox.ItemTemplate>
|
|
</ComboBox>
|
|
|
|
<TextBlock Text="Aspect Mode"
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
Margin="0,12,0,4"/>
|
|
<ComboBox ItemsSource="{Binding Settings.AvailableAspectModes}"
|
|
SelectedItem="{Binding Settings.Aspect}">
|
|
<ComboBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
|
</DataTemplate>
|
|
</ComboBox.ItemTemplate>
|
|
</ComboBox>
|
|
|
|
<TextBlock Text="Audio Mode"
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
Margin="0,12,0,4"/>
|
|
<ComboBox ItemsSource="{Binding Settings.AvailableAudioModes}"
|
|
SelectedItem="{Binding Settings.Audio}">
|
|
<ComboBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
|
</DataTemplate>
|
|
</ComboBox.ItemTemplate>
|
|
</ComboBox>
|
|
|
|
<!-- · · NDI Network · · -->
|
|
<TextBlock Text="NDI NETWORK"
|
|
Style="{StaticResource Text.Caption}"
|
|
Margin="0,28,0,0"/>
|
|
|
|
<TextBlock Text="Discovery group"
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
Margin="0,8,0,4"/>
|
|
<TextBox Text="{Binding Settings.DiscoveryGroups, UpdateSourceTrigger=PropertyChanged}"/>
|
|
<TextBlock Text="Receive sources from this group. Empty = Public."
|
|
Style="{StaticResource Text.Body}"
|
|
FontSize="11"
|
|
Foreground="{DynamicResource Text.Tertiary}"
|
|
Margin="0,4,0,0"/>
|
|
|
|
<TextBlock Text="Output group"
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
Margin="0,12,0,4"/>
|
|
<TextBox Text="{Binding Settings.OutputGroups, UpdateSourceTrigger=PropertyChanged}"/>
|
|
<TextBlock Text="Broadcast TeamsISO outputs on this group. Empty = Public."
|
|
Style="{StaticResource Text.Body}"
|
|
FontSize="11"
|
|
Foreground="{DynamicResource Text.Tertiary}"
|
|
Margin="0,4,0,0"/>
|
|
|
|
<Border Background="{DynamicResource Stone.SurfaceElevated}"
|
|
BorderBrush="{DynamicResource Stone.Border}"
|
|
BorderThickness="1"
|
|
CornerRadius="{StaticResource Radius.M}"
|
|
Padding="12,10"
|
|
Margin="0,12,0,0">
|
|
<TextBlock Style="{StaticResource Text.Body}"
|
|
FontSize="11"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
TextWrapping="Wrap"
|
|
Text="Group changes apply on next launch — running pipelines aren't restarted to avoid orphaning your live ISOs."/>
|
|
</Border>
|
|
|
|
<!-- · · Display · · -->
|
|
<TextBlock Text="DISPLAY"
|
|
Style="{StaticResource Text.Caption}"
|
|
Margin="0,28,0,0"/>
|
|
|
|
<CheckBox Content="Hide my self-preview from participants"
|
|
IsChecked="{Binding Settings.HideLocalSelf}"
|
|
Margin="0,8,0,0"/>
|
|
|
|
<Button Style="{StaticResource Button.Primary}"
|
|
Content="Apply Changes"
|
|
Command="{Binding Settings.ApplyCommand}"
|
|
HorizontalAlignment="Stretch"
|
|
Margin="0,28,0,0"
|
|
Padding="0,10"/>
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
</Border>
|
|
|
|
<!-- ════════ Main content: participants ════════ -->
|
|
<Grid Margin="32,28,32,28">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Section header with count -->
|
|
<StackPanel Grid.Row="0" Orientation="Horizontal" VerticalAlignment="Center">
|
|
<TextBlock Text="Participants"
|
|
Style="{StaticResource Text.Heading}"
|
|
FontSize="18"/>
|
|
<Border Background="{DynamicResource Stone.Surface}"
|
|
BorderBrush="{DynamicResource Stone.Border}"
|
|
BorderThickness="1"
|
|
CornerRadius="999"
|
|
Padding="9,2"
|
|
Margin="12,0,0,0"
|
|
VerticalAlignment="Center">
|
|
<TextBlock Text="{Binding Participants.Count}"
|
|
Style="{StaticResource Text.Mono}"
|
|
Foreground="{DynamicResource Text.Secondary}"
|
|
FontSize="11"/>
|
|
</Border>
|
|
</StackPanel>
|
|
|
|
<TextBlock Grid.Row="1"
|
|
Text="Toggle the ISO column to spin up an isolated, normalized NDI output for any participant."
|
|
Style="{StaticResource Text.Body}"
|
|
Foreground="{DynamicResource Text.Tertiary}"
|
|
Margin="0,6,0,18"/>
|
|
|
|
<!-- Participants table inside a card -->
|
|
<Border Grid.Row="2"
|
|
Style="{StaticResource Card}"
|
|
Padding="0">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<DataGrid Grid.Row="1"
|
|
ItemsSource="{Binding Participants}"
|
|
Margin="4,4,4,4">
|
|
<DataGrid.Columns>
|
|
<DataGridTemplateColumn Header="Display Name" Width="2*">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
<Border Width="28" Height="28"
|
|
CornerRadius="14"
|
|
Background="{DynamicResource Accent.OrangeMuted}">
|
|
<TextBlock Text="●"
|
|
FontSize="9"
|
|
Foreground="{DynamicResource Accent.Orange}"
|
|
HorizontalAlignment="Center"
|
|
VerticalAlignment="Center"/>
|
|
</Border>
|
|
<TextBlock Text="{Binding DisplayName}"
|
|
Style="{StaticResource Text.Body}"
|
|
FontWeight="Medium"
|
|
Margin="12,0,0,0"
|
|
VerticalAlignment="Center"/>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
|
|
<DataGridTemplateColumn Header="Source" Width="*">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<TextBlock Text="{Binding SourceMachine}"
|
|
Style="{StaticResource Text.Mono}"
|
|
VerticalAlignment="Center"/>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
|
|
<DataGridTemplateColumn Header="Output Name" Width="2*">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<TextBox Text="{Binding CustomName, UpdateSourceTrigger=PropertyChanged}"
|
|
Padding="8,6"
|
|
Margin="0,0,12,0"
|
|
VerticalAlignment="Center"/>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
|
|
<DataGridTemplateColumn Header="ISO" Width="120">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<Button Command="{Binding ToggleIsoCommand}"
|
|
Margin="0,0,12,0">
|
|
<Button.Style>
|
|
<Style TargetType="Button" BasedOn="{StaticResource Button.IsoToggle}">
|
|
<Setter Property="Content" Value="Enable"/>
|
|
<Setter Property="Foreground" Value="{DynamicResource Text.Secondary}"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsEnabled}" Value="True">
|
|
<Setter Property="Content" Value="● Live"/>
|
|
<Setter Property="Background" Value="#1F3A1F"/>
|
|
<Setter Property="BorderBrush" Value="{DynamicResource Status.Good}"/>
|
|
<Setter Property="Foreground" Value="{DynamicResource Status.Good}"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsProcessing}" Value="True">
|
|
<Setter Property="Content" Value="…"/>
|
|
<Setter Property="IsEnabled" Value="False"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Button.Style>
|
|
</Button>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
</DataGrid.Columns>
|
|
</DataGrid>
|
|
</Grid>
|
|
</Border>
|
|
</Grid>
|
|
|
|
</DockPanel>
|
|
</Window>
|