using VoiceCat.App.Audio; namespace VoiceCat.App.Forms; /// /// Modal dialog for selecting which apps' audio to share. /// Returns the chosen via , /// or if the user dismissed without sharing. /// public sealed class AppAudioPickerDialog : Form { private readonly RadioButton _rdoAll; private readonly RadioButton _rdoOnly; private readonly RadioButton _rdoExcept; private readonly ListView _appList; private readonly Label _lblApps; // Snapshot taken when the dialog opens (refresh on open, not on every check change). private IReadOnlyList _apps = []; public AppAudioScope? ChosenScope { get; private set; } public AppAudioPickerDialog() { // ── Radio buttons ─────────────────────────────────────────────────── _rdoAll = new RadioButton { Text = "&Entire desktop", Checked = true, Location = new Point(12, 12), Size = new Size(360, 20), TabIndex = 0, }; _rdoOnly = new RadioButton { Text = "&Only selected apps", Location = new Point(12, 36), Size = new Size(360, 20), TabIndex = 1, }; _rdoExcept = new RadioButton { Text = "All apps e&xcept selected", Location = new Point(12, 60), Size = new Size(360, 20), TabIndex = 2, }; _rdoAll.CheckedChanged += OnModeChanged; _rdoOnly.CheckedChanged += OnModeChanged; _rdoExcept.CheckedChanged += OnModeChanged; // ── App list ──────────────────────────────────────────────────────── _lblApps = new Label { Text = "Apps with active audio sessions:", AutoSize = true, Location = new Point(12, 90), Visible = false, TabIndex = 3, }; _appList = new ListView { Location = new Point(12, 112), Size = new Size(360, 180), CheckBoxes = true, View = View.List, Visible = false, TabIndex = 4, FullRowSelect = true, }; // ── Buttons ───────────────────────────────────────────────────────── var btnShare = new Button { Text = "&Share", DialogResult = DialogResult.OK, Location = new Point(216, 308), Size = new Size(75, 27), TabIndex = 5, }; var btnCancel = new Button { Text = "&Cancel", DialogResult = DialogResult.Cancel, Location = new Point(297, 308), Size = new Size(75, 27), TabIndex = 6, }; btnShare.Click += OnShareClick; AcceptButton = btnShare; CancelButton = btnCancel; AutoScaleMode = AutoScaleMode.Font; ClientSize = new Size(384, 348); Controls.AddRange([_rdoAll, _rdoOnly, _rdoExcept, _lblApps, _appList, btnShare, btnCancel]); FormBorderStyle = FormBorderStyle.FixedDialog; MaximizeBox = false; MinimizeBox = false; StartPosition = FormStartPosition.CenterParent; Text = "Share App Audio"; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); RefreshAppList(); } private void RefreshAppList() { _apps = AudioSessionEnumerator.GetAudioApps(); _appList.Items.Clear(); foreach (var app in _apps) _appList.Items.Add(new ListViewItem($"{app.DisplayName} (PID {app.Pid})") { Tag = app.Pid }); } private void OnModeChanged(object? sender, EventArgs e) { bool showList = _rdoOnly.Checked || _rdoExcept.Checked; _lblApps.Visible = showList; _appList.Visible = showList; if (showList && _appList.Items.Count == 0) RefreshAppList(); } private void OnShareClick(object? sender, EventArgs e) { if (_rdoAll.Checked) { ChosenScope = new EntireDesktop(); return; } var checkedPids = new List(); var checkedNames = new List(); foreach (ListViewItem item in _appList.CheckedItems) { if (item.Tag is int pid) { checkedPids.Add(pid); checkedNames.Add(item.Text); } } if (checkedPids.Count == 0) { MessageBox.Show( "Select at least one app, or choose 'Entire desktop'.", "No apps selected", MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.None; // prevent close return; } ChosenScope = _rdoOnly.Checked ? new OnlyApps(checkedPids, checkedNames) : new AllExceptApps(checkedPids, checkedNames); } /// All apps that were visible in the list when the user clicked Share. public IReadOnlyList VisibleApps => _apps; }