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>
This commit is contained in:
176
clients/windows/VoiceCat.App/Forms/MainForm.Designer.cs
generated
176
clients/windows/VoiceCat.App/Forms/MainForm.Designer.cs
generated
@@ -4,8 +4,11 @@ partial class MainForm
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null!;
|
||||
|
||||
// Menu bar
|
||||
// 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!;
|
||||
@@ -20,23 +23,20 @@ partial class MainForm
|
||||
private Label lblUsers = null!;
|
||||
private ListBox lstUsers = null!;
|
||||
|
||||
// Right panel: chat transcript, compose row, activity log
|
||||
// Right panel: unified log, compose row, output volume
|
||||
private TableLayoutPanel tblRight = null!;
|
||||
private Label lblChat = null!;
|
||||
private RichTextBox rtbChat = null!;
|
||||
private Label lblLog = null!;
|
||||
private RichTextBox rtbLog = null!;
|
||||
private TableLayoutPanel tblCompose = null!;
|
||||
private ComboBox cboScope = null!;
|
||||
private TextBox txtCompose = null!;
|
||||
private Button btnSend = null!;
|
||||
private Label lblActivity = null!;
|
||||
private ListBox lstActivity = 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 Button btnMicToggle = null!;
|
||||
private Button btnScreenShareToggle = null!;
|
||||
private CheckBox chkMute = null!;
|
||||
private CheckBox chkDeafen = null!;
|
||||
private RadioButton radioVad = null!;
|
||||
@@ -70,19 +70,16 @@ partial class MainForm
|
||||
lblUsers = new Label();
|
||||
lstUsers = new ListBox();
|
||||
tblRight = new TableLayoutPanel();
|
||||
lblChat = new Label();
|
||||
rtbChat = new RichTextBox();
|
||||
lblLog = new Label();
|
||||
rtbLog = new RichTextBox();
|
||||
tblCompose = new TableLayoutPanel();
|
||||
cboScope = new ComboBox();
|
||||
txtCompose = new TextBox();
|
||||
btnSend = new Button();
|
||||
lblActivity = new Label();
|
||||
lstActivity = new ListBox();
|
||||
lblOutputVolume = new Label();
|
||||
trkOutputVolume = new TrackBar();
|
||||
pnlVoice = new Panel();
|
||||
flpVoiceTop = new FlowLayoutPanel();
|
||||
flpVoiceBottom = new FlowLayoutPanel();
|
||||
btnMicToggle = new Button();
|
||||
btnScreenShareToggle = new Button();
|
||||
chkMute = new CheckBox();
|
||||
chkDeafen = new CheckBox();
|
||||
radioVad = new RadioButton();
|
||||
@@ -97,6 +94,10 @@ partial class MainForm
|
||||
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";
|
||||
@@ -144,78 +145,72 @@ partial class MainForm
|
||||
splitLeft.Panel2.Controls.Add(lstUsers);
|
||||
splitLeft.Panel2.Controls.Add(lblUsers);
|
||||
|
||||
// ── Chat transcript ───────────────────────────────────────────────────
|
||||
lblChat.Text = "Chat:";
|
||||
lblChat.AutoSize = true;
|
||||
lblChat.Padding = new Padding(2, 2, 2, 1);
|
||||
// ── Unified log ───────────────────────────────────────────────────────
|
||||
lblLog.Text = "Chat & Activity:";
|
||||
lblLog.AutoSize = true;
|
||||
lblLog.Padding = new Padding(2, 2, 2, 1);
|
||||
|
||||
rtbChat.AccessibleName = "Chat transcript";
|
||||
rtbChat.AccessibleDescription = "History of channel and private messages.";
|
||||
rtbChat.Dock = DockStyle.Fill;
|
||||
rtbChat.ReadOnly = true;
|
||||
rtbChat.ScrollBars = RichTextBoxScrollBars.Vertical;
|
||||
rtbChat.BackColor = SystemColors.Window;
|
||||
rtbChat.TabIndex = 0;
|
||||
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 ───────────────────────────────────────────────────────
|
||||
cboScope.AccessibleName = "Send to";
|
||||
cboScope.AccessibleDescription =
|
||||
"Choose Channel to send to everyone, or a specific user for a private message.";
|
||||
cboScope.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
cboScope.Dock = DockStyle.Fill;
|
||||
cboScope.TabIndex = 1;
|
||||
|
||||
txtCompose.AccessibleName = "Message text";
|
||||
txtCompose.AccessibleDescription = "Type your message. Press Enter or click Send to send.";
|
||||
txtCompose.AccessibleDescription = "Type your channel message. Press Enter or click Send to send.";
|
||||
txtCompose.Dock = DockStyle.Fill;
|
||||
txtCompose.TabIndex = 2;
|
||||
txtCompose.TabIndex = 1;
|
||||
|
||||
btnSend.Text = "&Send";
|
||||
btnSend.Dock = DockStyle.Fill;
|
||||
btnSend.TabIndex = 3;
|
||||
btnSend.TabIndex = 2;
|
||||
|
||||
tblCompose.ColumnCount = 3;
|
||||
tblCompose.ColumnCount = 2;
|
||||
tblCompose.RowCount = 1;
|
||||
tblCompose.Dock = DockStyle.Fill;
|
||||
tblCompose.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 165F));
|
||||
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(cboScope, 0, 0);
|
||||
tblCompose.Controls.Add(txtCompose, 1, 0);
|
||||
tblCompose.Controls.Add(btnSend, 2, 0);
|
||||
tblCompose.Controls.Add(txtCompose, 0, 0);
|
||||
tblCompose.Controls.Add(btnSend, 1, 0);
|
||||
|
||||
// ── Activity log ──────────────────────────────────────────────────────
|
||||
lblActivity.Text = "Activity:";
|
||||
lblActivity.AutoSize = true;
|
||||
lblActivity.Padding = new Padding(2, 4, 2, 1);
|
||||
// ── Output volume row ─────────────────────────────────────────────────
|
||||
lblOutputVolume.Text = "Output volume:";
|
||||
lblOutputVolume.AutoSize = true;
|
||||
lblOutputVolume.Padding = new Padding(2, 6, 4, 0);
|
||||
|
||||
lstActivity.AccessibleName = "Activity log";
|
||||
lstActivity.AccessibleDescription =
|
||||
"Record of joins, leaves, talk-state changes, and server messages.";
|
||||
lstActivity.Dock = DockStyle.Fill;
|
||||
lstActivity.HorizontalScrollbar = true;
|
||||
lstActivity.TabIndex = 4;
|
||||
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));
|
||||
tblRight.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
|
||||
tblRight.RowStyles.Add(new RowStyle(SizeType.Absolute, 34F));
|
||||
tblRight.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||||
tblRight.RowStyles.Add(new RowStyle(SizeType.Absolute, 120F));
|
||||
tblRight.Controls.Add(lblChat, 0, 0);
|
||||
tblRight.Controls.Add(rtbChat, 0, 1);
|
||||
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(lblActivity, 0, 3);
|
||||
tblRight.Controls.Add(lstActivity, 0, 4);
|
||||
tblRight.Controls.Add(lblOutputVolume, 0, 3);
|
||||
tblRight.Controls.Add(trkOutputVolume, 0, 4);
|
||||
|
||||
// ── Main split ────────────────────────────────────────────────────────
|
||||
// SplitterDistance set in OnLoad — see MainForm.cs.
|
||||
splitMain.Dock = DockStyle.Fill;
|
||||
splitMain.Panel1MinSize = 150;
|
||||
splitMain.TabIndex = 1;
|
||||
@@ -223,26 +218,6 @@ partial class MainForm
|
||||
splitMain.Panel2.Controls.Add(tblRight);
|
||||
|
||||
// ── Voice control panel ───────────────────────────────────────────────
|
||||
// Top row: mic start/stop, mute/deafen, VAD/PTT
|
||||
btnMicToggle.Text = "&Join Voice";
|
||||
btnMicToggle.AutoSize = true;
|
||||
btnMicToggle.Margin = new Padding(0, 2, 6, 0);
|
||||
btnMicToggle.TabIndex = 0;
|
||||
|
||||
// Screen-audio share — independent of mic voice (can share without joining voice and
|
||||
// vice versa). The core's WASAPI loopback path (VOICECAT_HAS_LOOPBACK, always on for
|
||||
// the windows-client preset) captures the default render endpoint; see docs/voice.md
|
||||
// §9. Whole-device loopback inherently re-captures this app's own incoming voice mix —
|
||||
// an accepted self-echo characteristic, not a bug.
|
||||
btnScreenShareToggle.Text = "Share Screen &Audio";
|
||||
btnScreenShareToggle.AccessibleName = "Share screen audio";
|
||||
btnScreenShareToggle.AccessibleDescription =
|
||||
"Start or stop sharing your computer's audio (desktop/system audio) with the channel. " +
|
||||
"Independent of the microphone. Captures everything playing through your default speakers.";
|
||||
btnScreenShareToggle.AutoSize = true;
|
||||
btnScreenShareToggle.Margin = new Padding(0, 2, 12, 0);
|
||||
btnScreenShareToggle.TabIndex = 10;
|
||||
|
||||
chkMute.Text = "&Mute mic";
|
||||
chkMute.AutoSize = true;
|
||||
chkMute.Enabled = false;
|
||||
@@ -291,8 +266,6 @@ partial class MainForm
|
||||
flpVoiceTop.Height = 34;
|
||||
flpVoiceTop.AutoSize = false;
|
||||
flpVoiceTop.Padding = new Padding(4, 2, 4, 0);
|
||||
flpVoiceTop.Controls.Add(btnMicToggle);
|
||||
flpVoiceTop.Controls.Add(btnScreenShareToggle);
|
||||
flpVoiceTop.Controls.Add(chkMute);
|
||||
flpVoiceTop.Controls.Add(chkDeafen);
|
||||
flpVoiceTop.Controls.Add(lblMode);
|
||||
@@ -330,12 +303,12 @@ partial class MainForm
|
||||
pbLevel.Maximum = 100;
|
||||
pbLevel.Margin = new Padding(0, 6, 0, 0);
|
||||
pbLevel.Style = ProgressBarStyle.Continuous;
|
||||
pbLevel.TabStop = false; // informational, not actionable
|
||||
pbLevel.TabStop = false;
|
||||
|
||||
lblVadThreshold.Text = "Sensitivity:";
|
||||
lblVadThreshold.AutoSize = true;
|
||||
lblVadThreshold.Margin = new Padding(12, 5, 4, 0);
|
||||
lblVadThreshold.Visible = true; // shown when VAD mode active
|
||||
lblVadThreshold.Visible = true;
|
||||
|
||||
trkVadThreshold.AccessibleName = "VAD sensitivity";
|
||||
trkVadThreshold.AccessibleDescription =
|
||||
@@ -343,7 +316,7 @@ partial class MainForm
|
||||
"Range 1–100; default 25.";
|
||||
trkVadThreshold.Minimum = 1;
|
||||
trkVadThreshold.Maximum = 100;
|
||||
trkVadThreshold.Value = 76; // maps to ~0.024 (≈ default 0.025 threshold)
|
||||
trkVadThreshold.Value = 76;
|
||||
trkVadThreshold.TickFrequency = 10;
|
||||
trkVadThreshold.SmallChange = 1;
|
||||
trkVadThreshold.LargeChange = 10;
|
||||
@@ -369,8 +342,23 @@ partial class MainForm
|
||||
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 = new MenuStrip();
|
||||
menuStrip.AccessibleName = "Main menu";
|
||||
menuStrip.Dock = DockStyle.Top;
|
||||
menuStrip.TabIndex = 0;
|
||||
@@ -380,12 +368,12 @@ partial class MainForm
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(960, 680);
|
||||
MinimumSize = new Size(700, 540);
|
||||
KeyPreview = true; // form sees KeyDown/KeyUp before focused control (needed for PTT)
|
||||
// Controls added in reverse docking priority: Fill first, then Bottom, then Top.
|
||||
Controls.Add(splitMain); // DockStyle.Fill
|
||||
Controls.Add(pnlVoice); // DockStyle.Bottom
|
||||
Controls.Add(lblStatus); // DockStyle.Top
|
||||
Controls.Add(menuStrip); // DockStyle.Top (placed at the very top)
|
||||
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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user