diff --git a/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs b/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs index a03cca3..22e6443 100644 --- a/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs +++ b/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs @@ -213,22 +213,35 @@ public sealed partial class MainWindow : Window } /// - /// Minimal participant row — name + ISO state + toggle button. Drops - /// the brushed avatar / theme-resource lookups that may have been - /// triggering the crash. The full visual row template comes back - /// after we've verified the binding path works. + /// Participant row — name + ISO state pill, with the cyan-accent + /// left border when the participant is the active speaker, and the + /// pill background flipping green/coral/amber based on ISO state. + /// Imperative construction so we sidestep the WinUI 3 DataTemplate + /// parser path that crashes on this build host. /// private static Microsoft.UI.Xaml.Controls.Grid BuildSimpleRow(ParticipantViewModel p) { var grid = new Microsoft.UI.Xaml.Controls.Grid { Height = 56, - Padding = new Thickness(20, 0, 20, 0), + Padding = new Thickness(0, 0, 20, 0), }; + grid.ColumnDefinitions.Add(new Microsoft.UI.Xaml.Controls.ColumnDefinition { Width = new Microsoft.UI.Xaml.GridLength(3) }); + grid.ColumnDefinitions.Add(new Microsoft.UI.Xaml.Controls.ColumnDefinition { Width = new Microsoft.UI.Xaml.GridLength(20) }); grid.ColumnDefinitions.Add(new Microsoft.UI.Xaml.Controls.ColumnDefinition { Width = new Microsoft.UI.Xaml.GridLength(1, Microsoft.UI.Xaml.GridUnitType.Star) }); - grid.ColumnDefinitions.Add(new Microsoft.UI.Xaml.Controls.ColumnDefinition { Width = new Microsoft.UI.Xaml.GridLength(120) }); + grid.ColumnDefinitions.Add(new Microsoft.UI.Xaml.Controls.ColumnDefinition { Width = new Microsoft.UI.Xaml.GridLength(140) }); grid.ColumnDefinitions.Add(new Microsoft.UI.Xaml.Controls.ColumnDefinition { Width = Microsoft.UI.Xaml.GridLength.Auto }); + // Active-speaker left accent. Visible only when IsActiveSpeaker + // flips on (driven by MainViewModel.OnStatsTick at 1Hz). + var accent = new Microsoft.UI.Xaml.Controls.Border + { + Background = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["AccentCyanText"], + Visibility = p.IsActiveSpeaker ? Visibility.Visible : Visibility.Collapsed, + }; + Microsoft.UI.Xaml.Controls.Grid.SetColumn(accent, 0); + grid.Children.Add(accent); + var nameStack = new Microsoft.UI.Xaml.Controls.StackPanel { VerticalAlignment = VerticalAlignment.Center, @@ -248,7 +261,7 @@ public sealed partial class MainWindow : Window }; nameStack.Children.Add(nameText); nameStack.Children.Add(codecText); - Microsoft.UI.Xaml.Controls.Grid.SetColumn(nameStack, 0); + Microsoft.UI.Xaml.Controls.Grid.SetColumn(nameStack, 2); grid.Children.Add(nameStack); var outputText = new Microsoft.UI.Xaml.Controls.TextBlock @@ -258,7 +271,7 @@ public sealed partial class MainWindow : Window FontSize = 12, VerticalAlignment = VerticalAlignment.Center, }; - Microsoft.UI.Xaml.Controls.Grid.SetColumn(outputText, 1); + Microsoft.UI.Xaml.Controls.Grid.SetColumn(outputText, 3); grid.Children.Add(outputText); var pillText = new Microsoft.UI.Xaml.Controls.TextBlock @@ -277,7 +290,8 @@ public sealed partial class MainWindow : Window VerticalAlignment = VerticalAlignment.Center, Content = pillText, }; - Microsoft.UI.Xaml.Controls.Grid.SetColumn(pill, 2); + ApplyIsoPillStyling(pill, pillText, p.IsoStateLabel); + Microsoft.UI.Xaml.Controls.Grid.SetColumn(pill, 4); grid.Children.Add(pill); p.PropertyChanged += (_, e) => @@ -298,6 +312,12 @@ public sealed partial class MainWindow : Window case nameof(ParticipantViewModel.IsoStateLabel): case nameof(ParticipantViewModel.IsEnabled): pillText.Text = p.IsoStateLabel; + ApplyIsoPillStyling(pill, pillText, p.IsoStateLabel); + break; + case nameof(ParticipantViewModel.IsActiveSpeaker): + accent.Visibility = p.IsActiveSpeaker + ? Visibility.Visible + : Visibility.Collapsed; break; } }); @@ -306,6 +326,49 @@ public sealed partial class MainWindow : Window return grid; } + /// + /// Re-color the ISO pill based on the engine's reported state. + /// LIVE = green on green-tinted background. ERROR = coral on coral- + /// tinted background. STARTING = amber. NO SIGNAL = amber. OFF = + /// neutral surface. Mirrors the WPF host's IsoToggle data-trigger + /// behavior but built imperatively from theme-resource brush keys. + /// + private static void ApplyIsoPillStyling( + Microsoft.UI.Xaml.Controls.Button pill, + Microsoft.UI.Xaml.Controls.TextBlock pillText, + string state) + { + Microsoft.UI.Xaml.Media.Brush bg, border, fg; + switch (state) + { + case "LIVE": + bg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["StatusLiveBg"]; + border = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["StatusLive"]; + fg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["StatusLive"]; + break; + case "ERROR": + bg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["AccentCoralBg"]; + border = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["AccentCoral"]; + fg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["AccentCoral"]; + break; + case "NO SIGNAL": + case "STARTING": + bg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["StatusWarnBg"]; + border = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["StatusWarn"]; + fg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["StatusWarn"]; + break; + default: // OFF / — + bg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["BgSurface"]; + border = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["BorderStrong"]; + fg = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["FgPrimary"]; + break; + } + pill.Background = bg; + pill.BorderBrush = border; + pill.BorderThickness = new Thickness(1); + pillText.Foreground = fg; + } + /// Full rich row template — replaces BuildSimpleRow once we've verified the simple version doesn't crash. private static Microsoft.UI.Xaml.Controls.Grid BuildParticipantRow(ParticipantViewModel p) {