Files
voice-cat/clients/windows/VoiceCat.App/Forms/MainForm.Designer.cs
Talon 97fa659422 feat(windows): UI overhaul -- toolbar, unified log, PM windows, channel counts, output volume
- Voice actions (Join Voice, Share Screen Audio) moved to a ToolStrip toolbar and
  a new Voice menu in the menu bar; removed from the bottom voice panel
- Activity log and chat log collapsed into a single RichTextBox (rtbLog); activity
  events appear in gray, chat messages in default color
- Private messaging reworked: each conversation opens in its own modeless
  PrivateMessageForm instead of sharing the main chat log via a scope dropdown;
  cboScope removed; main compose bar always sends to the current channel
- New "Messages -> New Private Message..." menu item (Ctrl+P) opens a UserPickerDialog
  listing all connected server users (not just the current channel) so you can PM
  anyone on the server
- Channel tree now shows live user counts, e.g. "General (3)" -- counts sourced from
  the existing _users dictionary which already tracks all server users with channel IDs
- Global output volume slider (TrackBar, 0-100, default 80) added to the right panel;
  wired to new vc_set_output_volume C ABI function that applies a master gain multiplier
  in the audio engine playback callback after mixing all streams
- vc_set_output_volume added end-to-end: voicecat.h, audio_engine.h/.cpp,
  client.h/.cpp, voicecat.cpp, NativeMethods.cs, VoiceCatClient.cs
- Documented Windows PowerShell ctest requirement in AGENTS.md and CLAUDE.md:
  MinGW binaries exit 0xc0000139 in Git Bash; always run ctest/.exe via PowerShell

22/22 ctest green (PowerShell); dotnet build 0 warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 14:24:54 +02:00

381 lines
16 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace VoiceCat.App.Forms;
partial class MainForm
{
private System.ComponentModel.IContainer components = null!;
// Menu bar + toolbar
private MenuStrip menuStrip = null!;
private ToolStrip toolStrip = null!;
private ToolStripButton tsbJoinVoice = null!;
private ToolStripButton tsbScreenShare = null!;
// Status bar
private Label lblStatus = null!;
// Main left/right split
private SplitContainer splitMain = null!;
// Left panel: channel tree on top, user list on bottom
private SplitContainer splitLeft = null!;
private Label lblChannels = null!;
private TreeView tvChannels = null!;
private Label lblUsers = null!;
private ListBox lstUsers = null!;
// Right panel: unified log, compose row, output volume
private TableLayoutPanel tblRight = null!;
private Label lblLog = null!;
private RichTextBox rtbLog = null!;
private TableLayoutPanel tblCompose = null!;
private TextBox txtCompose = null!;
private Button btnSend = null!;
private Label lblOutputVolume = null!;
private TrackBar trkOutputVolume = null!;
// Voice control panel (docked Bottom)
private Panel pnlVoice = null!;
private FlowLayoutPanel flpVoiceTop = null!;
private FlowLayoutPanel flpVoiceBottom = null!;
private CheckBox chkMute = null!;
private CheckBox chkDeafen = null!;
private RadioButton radioVad = null!;
private RadioButton radioPtt = null!;
private RadioButton radioAlwaysOn = null!;
private Label lblPttKey = null!;
private Button btnChangePtt = null!;
private Label lblInputDevice = null!;
private ComboBox cboInputDevice = null!;
private Button btnRefreshDevices = null!;
private Label lblLevel = null!;
private ProgressBar pbLevel = null!;
private Label lblVadThreshold = null!;
private TrackBar trkVadThreshold = null!;
protected override void Dispose(bool disposing)
{
if (disposing) components?.Dispose();
base.Dispose(disposing);
}
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
lblStatus = new Label();
splitMain = new SplitContainer();
splitLeft = new SplitContainer();
lblChannels = new Label();
tvChannels = new TreeView();
lblUsers = new Label();
lstUsers = new ListBox();
tblRight = new TableLayoutPanel();
lblLog = new Label();
rtbLog = new RichTextBox();
tblCompose = new TableLayoutPanel();
txtCompose = new TextBox();
btnSend = new Button();
lblOutputVolume = new Label();
trkOutputVolume = new TrackBar();
pnlVoice = new Panel();
flpVoiceTop = new FlowLayoutPanel();
flpVoiceBottom = new FlowLayoutPanel();
chkMute = new CheckBox();
chkDeafen = new CheckBox();
radioVad = new RadioButton();
radioPtt = new RadioButton();
radioAlwaysOn = new RadioButton();
lblPttKey = new Label();
btnChangePtt = new Button();
lblInputDevice = new Label();
cboInputDevice = new ComboBox();
btnRefreshDevices = new Button();
lblLevel = new Label();
pbLevel = new ProgressBar();
lblVadThreshold = new Label();
trkVadThreshold = new TrackBar();
menuStrip = new MenuStrip();
toolStrip = new ToolStrip();
tsbJoinVoice = new ToolStripButton();
tsbScreenShare = new ToolStripButton();
// ── Status label ──────────────────────────────────────────────────────
lblStatus.AccessibleName = "Connection status";
lblStatus.Dock = DockStyle.Top;
lblStatus.AutoSize = true;
lblStatus.Padding = new Padding(6, 4, 6, 4);
lblStatus.TabIndex = 0;
lblStatus.Text = "Connecting...";
// ── Channel tree ──────────────────────────────────────────────────────
lblChannels.Text = "Channels:";
lblChannels.Dock = DockStyle.Top;
lblChannels.AutoSize = true;
lblChannels.Padding = new Padding(4, 4, 4, 2);
tvChannels.AccessibleName = "Channel list";
tvChannels.AccessibleDescription =
"Double-click or press Enter to join a channel. " +
"Channels marked [password] require a password.";
tvChannels.Dock = DockStyle.Fill;
tvChannels.HideSelection = false;
tvChannels.TabIndex = 0;
// ── User list ─────────────────────────────────────────────────────────
lblUsers.Text = "Users in channel:";
lblUsers.Dock = DockStyle.Top;
lblUsers.AutoSize = true;
lblUsers.Padding = new Padding(4, 4, 4, 2);
lstUsers.AccessibleName = "Users in current channel";
lstUsers.AccessibleDescription =
"People in the same channel. Double-click or press Enter for per-user volume settings. " +
"Talking users are marked (talking).";
lstUsers.Dock = DockStyle.Fill;
lstUsers.TabIndex = 1;
// ── Left split (channels top, users bottom) ───────────────────────────
splitLeft.Orientation = Orientation.Horizontal;
splitLeft.Dock = DockStyle.Fill;
splitLeft.Panel1MinSize = 100;
splitLeft.Panel2MinSize = 80;
splitLeft.TabIndex = 0;
splitLeft.Panel1.Controls.Add(tvChannels);
splitLeft.Panel1.Controls.Add(lblChannels);
splitLeft.Panel2.Controls.Add(lstUsers);
splitLeft.Panel2.Controls.Add(lblUsers);
// ── Unified log ───────────────────────────────────────────────────────
lblLog.Text = "Chat & Activity:";
lblLog.AutoSize = true;
lblLog.Padding = new Padding(2, 2, 2, 1);
rtbLog.AccessibleName = "Chat and activity log";
rtbLog.AccessibleDescription = "Combined history of chat messages (normal) and activity events (gray).";
rtbLog.Dock = DockStyle.Fill;
rtbLog.ReadOnly = true;
rtbLog.ScrollBars = RichTextBoxScrollBars.Vertical;
rtbLog.BackColor = SystemColors.Window;
rtbLog.TabIndex = 0;
// ── Compose row ───────────────────────────────────────────────────────
txtCompose.AccessibleName = "Message text";
txtCompose.AccessibleDescription = "Type your channel message. Press Enter or click Send to send.";
txtCompose.Dock = DockStyle.Fill;
txtCompose.TabIndex = 1;
btnSend.Text = "&Send";
btnSend.Dock = DockStyle.Fill;
btnSend.TabIndex = 2;
tblCompose.ColumnCount = 2;
tblCompose.RowCount = 1;
tblCompose.Dock = DockStyle.Fill;
tblCompose.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tblCompose.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 68F));
tblCompose.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tblCompose.Padding = new Padding(0, 3, 0, 0);
tblCompose.Controls.Add(txtCompose, 0, 0);
tblCompose.Controls.Add(btnSend, 1, 0);
// ── Output volume row ─────────────────────────────────────────────────
lblOutputVolume.Text = "Output volume:";
lblOutputVolume.AutoSize = true;
lblOutputVolume.Padding = new Padding(2, 6, 4, 0);
trkOutputVolume.AccessibleName = "Output volume";
trkOutputVolume.AccessibleDescription = "Global playback volume for all incoming audio.";
trkOutputVolume.Minimum = 0;
trkOutputVolume.Maximum = 100;
trkOutputVolume.Value = 80;
trkOutputVolume.TickFrequency = 10;
trkOutputVolume.SmallChange = 1;
trkOutputVolume.LargeChange = 10;
trkOutputVolume.Dock = DockStyle.Fill;
trkOutputVolume.TabIndex = 3;
// ── Right table layout ────────────────────────────────────────────────
tblRight.ColumnCount = 1;
tblRight.RowCount = 5;
tblRight.Dock = DockStyle.Fill;
tblRight.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tblRight.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // row 0: lblLog
tblRight.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); // row 1: rtbLog
tblRight.RowStyles.Add(new RowStyle(SizeType.Absolute, 34F)); // row 2: compose
tblRight.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // row 3: vol label
tblRight.RowStyles.Add(new RowStyle(SizeType.Absolute, 45F)); // row 4: vol slider
tblRight.Controls.Add(lblLog, 0, 0);
tblRight.Controls.Add(rtbLog, 0, 1);
tblRight.Controls.Add(tblCompose, 0, 2);
tblRight.Controls.Add(lblOutputVolume, 0, 3);
tblRight.Controls.Add(trkOutputVolume, 0, 4);
// ── Main split ────────────────────────────────────────────────────────
splitMain.Dock = DockStyle.Fill;
splitMain.Panel1MinSize = 150;
splitMain.TabIndex = 1;
splitMain.Panel1.Controls.Add(splitLeft);
splitMain.Panel2.Controls.Add(tblRight);
// ── Voice control panel ───────────────────────────────────────────────
chkMute.Text = "&Mute mic";
chkMute.AutoSize = true;
chkMute.Enabled = false;
chkMute.Margin = new Padding(0, 4, 6, 0);
chkMute.TabIndex = 1;
chkDeafen.Text = "&Deafen";
chkDeafen.AutoSize = true;
chkDeafen.Enabled = false;
chkDeafen.Margin = new Padding(0, 4, 12, 0);
chkDeafen.TabIndex = 2;
var lblMode = new Label { Text = "Mode:", AutoSize = true, Margin = new Padding(0, 5, 4, 0) };
radioVad.Text = "&Voice activation";
radioVad.AutoSize = true;
radioVad.Checked = true;
radioVad.Enabled = false;
radioVad.Margin = new Padding(0, 4, 6, 0);
radioVad.TabIndex = 3;
radioPtt.Text = "&Push to talk";
radioPtt.AutoSize = true;
radioPtt.Enabled = false;
radioPtt.Margin = new Padding(0, 4, 4, 0);
radioPtt.TabIndex = 4;
radioAlwaysOn.Text = "A&lways on";
radioAlwaysOn.AutoSize = true;
radioAlwaysOn.Enabled = false;
radioAlwaysOn.Margin = new Padding(0, 4, 12, 0);
radioAlwaysOn.TabIndex = 5;
lblPttKey.Text = "(F8)";
lblPttKey.AutoSize = true;
lblPttKey.Margin = new Padding(2, 5, 4, 0);
lblPttKey.Visible = false;
btnChangePtt.Text = "Change key...";
btnChangePtt.AutoSize = true;
btnChangePtt.Margin = new Padding(0, 2, 0, 0);
btnChangePtt.Visible = false;
btnChangePtt.TabIndex = 6;
flpVoiceTop.Dock = DockStyle.Top;
flpVoiceTop.Height = 34;
flpVoiceTop.AutoSize = false;
flpVoiceTop.Padding = new Padding(4, 2, 4, 0);
flpVoiceTop.Controls.Add(chkMute);
flpVoiceTop.Controls.Add(chkDeafen);
flpVoiceTop.Controls.Add(lblMode);
flpVoiceTop.Controls.Add(radioVad);
flpVoiceTop.Controls.Add(radioPtt);
flpVoiceTop.Controls.Add(radioAlwaysOn);
flpVoiceTop.Controls.Add(lblPttKey);
flpVoiceTop.Controls.Add(btnChangePtt);
// Bottom row: device picker + level meter
lblInputDevice.Text = "Input:";
lblInputDevice.AutoSize = true;
lblInputDevice.Margin = new Padding(0, 5, 4, 0);
cboInputDevice.AccessibleName = "Input device";
cboInputDevice.AccessibleDescription = "Select which microphone or audio device to use.";
cboInputDevice.DropDownStyle = ComboBoxStyle.DropDownList;
cboInputDevice.Width = 200;
cboInputDevice.Margin = new Padding(0, 2, 4, 0);
cboInputDevice.TabIndex = 7;
btnRefreshDevices.Text = "Re&fresh";
btnRefreshDevices.AutoSize = true;
btnRefreshDevices.Margin = new Padding(0, 2, 12, 0);
btnRefreshDevices.TabIndex = 8;
lblLevel.Text = "Level:";
lblLevel.AutoSize = true;
lblLevel.Margin = new Padding(0, 5, 4, 0);
pbLevel.AccessibleName = "Microphone level";
pbLevel.AccessibleDescription = "Current input level from the microphone.";
pbLevel.Width = 120;
pbLevel.Height = 16;
pbLevel.Maximum = 100;
pbLevel.Margin = new Padding(0, 6, 0, 0);
pbLevel.Style = ProgressBarStyle.Continuous;
pbLevel.TabStop = false;
lblVadThreshold.Text = "Sensitivity:";
lblVadThreshold.AutoSize = true;
lblVadThreshold.Margin = new Padding(12, 5, 4, 0);
lblVadThreshold.Visible = true;
trkVadThreshold.AccessibleName = "VAD sensitivity";
trkVadThreshold.AccessibleDescription =
"Voice detection sensitivity. Higher = more sensitive (triggers on quieter sounds). " +
"Range 1100; default 25.";
trkVadThreshold.Minimum = 1;
trkVadThreshold.Maximum = 100;
trkVadThreshold.Value = 76;
trkVadThreshold.TickFrequency = 10;
trkVadThreshold.SmallChange = 1;
trkVadThreshold.LargeChange = 10;
trkVadThreshold.Width = 120;
trkVadThreshold.Margin = new Padding(0, 2, 0, 0);
trkVadThreshold.TabIndex = 9;
trkVadThreshold.Visible = true;
flpVoiceBottom.Dock = DockStyle.Fill;
flpVoiceBottom.Padding = new Padding(4, 0, 4, 2);
flpVoiceBottom.Controls.Add(lblInputDevice);
flpVoiceBottom.Controls.Add(cboInputDevice);
flpVoiceBottom.Controls.Add(btnRefreshDevices);
flpVoiceBottom.Controls.Add(lblLevel);
flpVoiceBottom.Controls.Add(pbLevel);
flpVoiceBottom.Controls.Add(lblVadThreshold);
flpVoiceBottom.Controls.Add(trkVadThreshold);
pnlVoice.Dock = DockStyle.Bottom;
pnlVoice.Height = 68;
pnlVoice.BorderStyle = BorderStyle.FixedSingle;
pnlVoice.Padding = new Padding(0);
pnlVoice.Controls.Add(flpVoiceBottom); // Fill — added first
pnlVoice.Controls.Add(flpVoiceTop); // Top — added last
// ── Toolbar ───────────────────────────────────────────────────────────
tsbJoinVoice.Text = "Join Voice";
tsbJoinVoice.DisplayStyle = ToolStripItemDisplayStyle.Text;
tsbJoinVoice.CheckOnClick = false;
tsbScreenShare.Text = "Share Screen Audio";
tsbScreenShare.DisplayStyle = ToolStripItemDisplayStyle.Text;
tsbScreenShare.CheckOnClick = false;
toolStrip.Dock = DockStyle.Top;
toolStrip.Items.Add(tsbJoinVoice);
toolStrip.Items.Add(new ToolStripSeparator());
toolStrip.Items.Add(tsbScreenShare);
toolStrip.TabIndex = 1;
toolStrip.AccessibleName = "Toolbar";
// ── Menu strip ────────────────────────────────────────────────────────
menuStrip.AccessibleName = "Main menu";
menuStrip.Dock = DockStyle.Top;
menuStrip.TabIndex = 0;
MainMenuStrip = menuStrip;
// ── Form ──────────────────────────────────────────────────────────────
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(960, 680);
MinimumSize = new Size(700, 540);
KeyPreview = true;
Controls.Add(splitMain); // DockStyle.Fill
Controls.Add(pnlVoice); // DockStyle.Bottom
Controls.Add(lblStatus); // DockStyle.Top
Controls.Add(toolStrip); // DockStyle.Top (above status)
Controls.Add(menuStrip); // DockStyle.Top (topmost)
StartPosition = FormStartPosition.CenterScreen;
Text = "VoiceCat";
}
}