Files
voice-cat/clients/windows/VoiceCat.App/Forms/AppAudioPickerDialog.cs

167 lines
5.6 KiB
C#
Raw Permalink Normal View History

using VoiceCat.App.Audio;
namespace VoiceCat.App.Forms;
/// <summary>
/// Modal dialog for selecting which apps' audio to share.
/// Returns the chosen <see cref="AppAudioScope"/> via <see cref="ChosenScope"/>,
/// or <see cref="DialogResult.Cancel"/> if the user dismissed without sharing.
/// </summary>
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<AudioAppInfo> _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<int>();
var checkedNames = new List<string>();
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);
}
/// <summary>All apps that were visible in the list when the user clicked Share.</summary>
public IReadOnlyList<AudioAppInfo> VisibleApps => _apps;
}