feat(ui): add MainWindow XAML with participants DataGrid, settings sidebar, alert banner
Some checks failed
CI / build-and-test (push) Failing after 34s

This commit is contained in:
Zac Gaetano 2026-05-07 15:40:49 +00:00
parent 8c441318d8
commit d64b110550
4 changed files with 231 additions and 6 deletions

View file

@ -0,0 +1,18 @@
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace TeamsISO.App.Converters;
[ValueConversion(typeof(bool), typeof(Visibility))]
public sealed class BoolToVisibilityConverter : IValueConverter
{
public Visibility TrueValue { get; set; } = Visibility.Visible;
public Visibility FalseValue { get; set; } = Visibility.Collapsed;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
value is true ? TrueValue : FalseValue;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
}

View file

@ -0,0 +1,52 @@
using System.Globalization;
using System.Windows.Data;
using TeamsISO.Engine.Domain;
namespace TeamsISO.App.Converters;
/// <summary>
/// Renders engine enum values into operator-friendly strings.
/// </summary>
public sealed class EnumDescriptionConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => value switch
{
TargetFramerate fr => fr switch
{
TargetFramerate.Fps23_976 => "23.976 fps",
TargetFramerate.Fps24 => "24 fps",
TargetFramerate.Fps25 => "25 fps",
TargetFramerate.Fps29_97 => "29.97 fps",
TargetFramerate.Fps30 => "30 fps",
TargetFramerate.Fps50 => "50 fps",
TargetFramerate.Fps59_94 => "59.94 fps",
TargetFramerate.Fps60 => "60 fps",
_ => fr.ToString()
},
TargetResolution r => r switch
{
TargetResolution.R720p => "720p",
TargetResolution.R1080p => "1080p",
TargetResolution.R4K => "4K",
_ => r.ToString()
},
AspectMode a => a switch
{
AspectMode.Pillarbox => "Pillarbox",
AspectMode.Letterbox => "Letterbox",
AspectMode.Stretch => "Stretch",
_ => a.ToString()
},
AudioMode m => m switch
{
AudioMode.Auto => "Auto (isolated → mixed fallback)",
AudioMode.Isolated => "Isolated",
AudioMode.Mixed => "Mixed",
_ => m.ToString()
},
_ => value
};
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
throw new NotSupportedException();
}

View file

@ -1,10 +1,159 @@
<Window x:Class="TeamsISO.App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TeamsISO" Height="600" Width="900">
<Grid>
<TextBlock Text="TeamsISO — Phase A scaffold (UI implemented in Phase C)"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="16" Foreground="#444"/>
</Grid>
xmlns:vm="clr-namespace:TeamsISO.App.ViewModels"
xmlns:conv="clr-namespace:TeamsISO.App.Converters"
Title="TeamsISO" Height="700" Width="1100"
Background="#202225" Foreground="#E8E8E8">
<Window.Resources>
<conv:BoolToVisibilityConverter x:Key="BoolToVis" />
<conv:EnumDescriptionConverter x:Key="EnumDesc" />
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#E8E8E8"/>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="13"/>
</Style>
<Style TargetType="Button">
<Setter Property="Padding" Value="10,4"/>
<Setter Property="Margin" Value="0,4,0,0"/>
<Setter Property="Background" Value="#3F4147"/>
<Setter Property="Foreground" Value="#E8E8E8"/>
<Setter Property="BorderBrush" Value="#5A5C63"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="Margin" Value="0,2,0,8"/>
</Style>
</Window.Resources>
<DockPanel>
<!-- Alert banner -->
<Border DockPanel.Dock="Top"
Background="#7A2B2B"
Padding="12,8"
Visibility="{Binding AlertBanner.IsVisible, Converter={StaticResource BoolToVis}}">
<DockPanel>
<Button DockPanel.Dock="Right"
Content="Dismiss"
Command="{Binding AlertBanner.DismissCommand}"
Margin="12,0,0,0"/>
<TextBlock Text="{Binding AlertBanner.Message}"
FontWeight="SemiBold"
VerticalAlignment="Center"/>
</DockPanel>
</Border>
<!-- Status footer -->
<Border DockPanel.Dock="Bottom" Background="#191B1F" Padding="10,6">
<TextBlock Text="{Binding StatusText}" FontStyle="Italic" Foreground="#A0A0A0"/>
</Border>
<!-- Settings sidebar -->
<Border DockPanel.Dock="Right" Background="#2A2C32" Width="320" Padding="14">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Text="Global Settings" FontSize="16" FontWeight="Bold" Margin="0,0,0,12"/>
<TextBlock Text="Target Framerate"/>
<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"/>
<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"/>
<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"/>
<ComboBox ItemsSource="{Binding Settings.AvailableAudioModes}"
SelectedItem="{Binding Settings.Audio}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Apply" Command="{Binding Settings.ApplyCommand}" Margin="0,16,0,0"/>
</StackPanel>
</ScrollViewer>
</Border>
<!-- Main content: participants list -->
<Grid Margin="14">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="Participants"
FontSize="16" FontWeight="Bold"
Margin="0,0,0,8"/>
<DataGrid Grid.Row="1"
ItemsSource="{Binding Participants}"
AutoGenerateColumns="False"
Background="#2A2C32"
Foreground="#E8E8E8"
RowBackground="#2A2C32"
AlternatingRowBackground="#2E3037"
BorderBrush="#5A5C63"
GridLinesVisibility="Horizontal"
HeadersVisibility="Column"
CanUserAddRows="False" CanUserDeleteRows="False"
RowHeight="34">
<DataGrid.Columns>
<DataGridTextColumn Header="Display Name" Binding="{Binding DisplayName}" Width="2*" IsReadOnly="True"/>
<DataGridTextColumn Header="Source" Binding="{Binding SourceMachine}" Width="*" IsReadOnly="True"/>
<DataGridTextColumn Header="ISO Output Name" Binding="{Binding CustomName, UpdateSourceTrigger=PropertyChanged}" Width="2*"/>
<DataGridTemplateColumn Header="ISO" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="{Binding IsEnabled, StringFormat={}{0}}" Command="{Binding ToggleIsoCommand}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content" Value="Enable"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled}" Value="True">
<Setter Property="Content" Value="Disable"/>
<Setter Property="Background" Value="#3D6F45"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsProcessing}" Value="True">
<Setter Property="Content" Value="…"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</DockPanel>
</Window>

View file

@ -1,4 +1,5 @@
using System.Windows;
using TeamsISO.App.ViewModels;
namespace TeamsISO.App;
@ -8,4 +9,9 @@ public partial class MainWindow : Window
{
InitializeComponent();
}
public MainWindow(MainViewModel viewModel) : this()
{
DataContext = viewModel;
}
}