Lands the approved shape brief as the WinUI 3 MainWindow:
* 64px left rail with brand mark, primary nav (participants), Teams
launch / hide / settings buttons, and the engine-status puck at the
bottom. All five rail buttons use Segoe Fluent Icons glyphs at a
uniform 20px optical size; no more bespoke <Path Data> shapes with
inconsistent stroke weights.
* 44px custom title bar via ExtendsContentIntoTitleBar +
SetTitleBar(AppTitleBar). The drag region absorbs the three live-state
pills inline (session timer 'live * 00:14:32', REC count + elapsed,
disk free) and a slim sun/moon theme-toggle button to the left of the
system Min/Max/Close controls. System buttons inherit ButtonForeground
Color etc. from AppWindow.TitleBar so they match palette in both
themes.
* Section header with 'Participants * count' display, filter input,
Refresh + Presets (Secondary buttons), and 'Enable all online' as
the single cyan Primary button - finally a real button hierarchy
instead of seven indistinguishable ghost buttons.
* Participants list rendered as ItemsRepeater + DataTemplate for now;
the CommunityToolkit DataGrid migration follows in a separate commit.
Row template at 64px height with: 3px cyan left border for active
speaker, avatar with initials in cyan-muted circle, name + codec line,
signal lock state with dot, audio meter via ProgressBar, output name
in JetBrains Mono, ISO state pill (LIVE/OFF/ERROR) at right.
* Conditional in-call control bar below the table: Mute / Camera /
Share / Marker / Leave + overflow kebab. Muted state binds the
destructive coral treatment to the Mute button; Leave is also
destructive (coral border + text); everything else is Secondary.
Tight 8px spacing keeps the bar dense without crowding.
* Slim 32px status bar at the bottom: control-surface URL on the left
(cyan dot indicator), keyboard-shortcut hints on the right in
tertiary mono. Replaces the WPF host's six-column footer.
Implementation notes:
* MockParticipant model populates the table with representative data
(Maya / Daniel / Aicha / Sam, one as active speaker) until the
ParticipantViewModel binding migrates over from the WPF host.
* Custom Program.cs takes ownership of Main from the XAML compiler
(DISABLE_XAML_GENERATED_MAIN). Calls Bootstrap.TryInitialize(0x00010006)
before Application.Start so the unpackaged .exe can locate the
WindowsAppSDK 1.6 framework MSIX at launch. Shutdown is paired in
a finally block.
* Theme toggle in code-behind flips Window.Content.RequestedTheme
between Dark and Light. {ThemeResource} bindings auto-swap across
the visual tree; system title-bar buttons (outside the XAML tree)
get color updates inline so they stay readable in both modes.
* app.manifest deferred from build - the framework-emitted manifest
covers DPI awareness and supportedOS GUIDs; reintroducing our own
goes in the next commit alongside the bootstrapper hardening.
Known issue: the unpackaged .exe currently fails to activate on this
build host with 'this application could not be started' before Main
runs. Build is clean; published output runs the same way. Diagnosing
the activation failure is the next session's first task (likely the
runtimeconfig.json including Microsoft.WindowsDesktop.App which WinUI 3
doesn't want, or a missing CRT redistributable). The WPF host remains
the running build until that's resolved.
dotnet build TeamsISO.Windows.slnf -c Debug: 0 warnings, 0 errors.
89 lines
4.4 KiB
C#
89 lines
4.4 KiB
C#
using Microsoft.UI;
|
|
using Microsoft.UI.Windowing;
|
|
using Microsoft.UI.Xaml;
|
|
using TeamsISO.App.WinUI.Models;
|
|
using Windows.Graphics;
|
|
using Windows.UI;
|
|
|
|
namespace TeamsISO.App.WinUI.Views;
|
|
|
|
public sealed partial class MainWindow : Window
|
|
{
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
|
|
Title = "TeamsISO";
|
|
|
|
// ── Custom title bar wiring ───────────────────────────────────────
|
|
// ExtendsContentIntoTitleBar=true tells WindowsAppSDK to draw the
|
|
// window chrome over our content instead of reserving a Windows-default
|
|
// caption strip. SetTitleBar marks AppTitleBar as the drag region —
|
|
// clicks on it route to the system drag handler, everything else stays
|
|
// hit-testable as a normal XAML element. The system min/max/close
|
|
// buttons render on top of the right edge regardless; we just provide
|
|
// their colors so they match our palette.
|
|
ExtendsContentIntoTitleBar = true;
|
|
SetTitleBar(AppTitleBar);
|
|
|
|
AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent;
|
|
AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
|
AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0xFF, 0x33, 0x34, 0x3A);
|
|
AppWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(0xFF, 0x3F, 0x3F, 0x47);
|
|
AppWindow.TitleBar.ButtonForegroundColor = Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF6);
|
|
AppWindow.TitleBar.ButtonHoverForegroundColor = Colors.White;
|
|
|
|
// ── Initial size & position ───────────────────────────────────────
|
|
// 1280x780 matches the WPF host's default — fits comfortably on a
|
|
// 14-inch laptop while giving the participants table 600+ pixels
|
|
// of vertical breathing room.
|
|
AppWindow.Resize(new SizeInt32(1280, 780));
|
|
|
|
// ── Mock data wiring (interim) ────────────────────────────────────
|
|
// Until ParticipantViewModel binds in the engine wiring commit, the
|
|
// table is populated from a static sample list so the visual design
|
|
// can be validated end-to-end against representative data.
|
|
ParticipantsRepeater.ItemsSource = MockParticipant.Sample();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cycle the active theme between Dark and Light. The actual swap target
|
|
/// is the window's root visual element — RequestedTheme on that element
|
|
/// propagates the {ThemeResource} swap across the whole tree, with no
|
|
/// flicker. The system title-bar buttons aren't part of the visual tree,
|
|
/// so their colors are recomputed inline.
|
|
///
|
|
/// Persistence to UIPreferences.Theme happens in the view-model wiring
|
|
/// commit (the WinUI host doesn't yet share UIPreferences with the WPF
|
|
/// host; both will once the WinUI host owns the .NET startup).
|
|
/// </summary>
|
|
private void OnThemeToggleClick(object sender, RoutedEventArgs e)
|
|
{
|
|
if (Content is FrameworkElement root)
|
|
{
|
|
var next = root.ActualTheme == ElementTheme.Dark
|
|
? ElementTheme.Light
|
|
: ElementTheme.Dark;
|
|
root.RequestedTheme = next;
|
|
|
|
// Title-bar system buttons read from AppWindow.TitleBar directly,
|
|
// not from the XAML resource dictionary, so we set them inline
|
|
// for both themes. Dark-mode chrome lands on near-white glyphs;
|
|
// light-mode chrome lands on near-black glyphs.
|
|
if (next == ElementTheme.Dark)
|
|
{
|
|
AppWindow.TitleBar.ButtonForegroundColor = Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF6);
|
|
AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0xFF, 0x33, 0x34, 0x3A);
|
|
AppWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(0xFF, 0x3F, 0x3F, 0x47);
|
|
ThemeToggleIcon.Glyph = ""; // sun
|
|
}
|
|
else
|
|
{
|
|
AppWindow.TitleBar.ButtonForegroundColor = Color.FromArgb(0xFF, 0x0A, 0x0A, 0x0A);
|
|
AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0xFF, 0xEC, 0xEE, 0xF1);
|
|
AppWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(0xFF, 0xE0, 0xE3, 0xE7);
|
|
ThemeToggleIcon.Glyph = ""; // moon
|
|
}
|
|
}
|
|
}
|
|
}
|