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:
115
clients/windows/VoiceCat.App/Forms/PrivateMessageForm.cs
Normal file
115
clients/windows/VoiceCat.App/Forms/PrivateMessageForm.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using VoiceCat.Interop;
|
||||
|
||||
namespace VoiceCat.App.Forms;
|
||||
|
||||
/// <summary>
|
||||
/// Modeless window for a single private message conversation.
|
||||
/// Created by MainForm and shown non-modally; MainForm routes incoming PMs here.
|
||||
/// </summary>
|
||||
public sealed class PrivateMessageForm : Form
|
||||
{
|
||||
private readonly VoiceCatClient _client;
|
||||
private readonly uint _userId;
|
||||
private readonly uint _selfUserId;
|
||||
|
||||
private readonly RichTextBox _rtbHistory;
|
||||
private readonly TextBox _txtCompose;
|
||||
private readonly Button _btnSend;
|
||||
|
||||
public PrivateMessageForm(VoiceCatClient client, uint userId, string nickname, uint selfUserId)
|
||||
{
|
||||
_client = client;
|
||||
_userId = userId;
|
||||
_selfUserId = selfUserId;
|
||||
|
||||
Text = $"Private Message — {nickname}";
|
||||
ClientSize = new Size(480, 360);
|
||||
MinimumSize = new Size(320, 240);
|
||||
StartPosition = FormStartPosition.Manual;
|
||||
|
||||
_rtbHistory = new RichTextBox
|
||||
{
|
||||
ReadOnly = true,
|
||||
Dock = DockStyle.Fill,
|
||||
ScrollBars = RichTextBoxScrollBars.Vertical,
|
||||
BackColor = SystemColors.Window,
|
||||
AccessibleName = "Message history",
|
||||
};
|
||||
|
||||
var composePanel = new TableLayoutPanel
|
||||
{
|
||||
Dock = DockStyle.Bottom,
|
||||
Height = 34,
|
||||
ColumnCount = 2,
|
||||
RowCount = 1,
|
||||
Padding = new Padding(0, 3, 0, 0),
|
||||
};
|
||||
composePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
|
||||
composePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 68F));
|
||||
composePanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
|
||||
|
||||
_txtCompose = new TextBox
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
AccessibleName = "Message text",
|
||||
};
|
||||
_btnSend = new Button
|
||||
{
|
||||
Text = "&Send",
|
||||
Dock = DockStyle.Fill,
|
||||
};
|
||||
|
||||
composePanel.Controls.Add(_txtCompose, 0, 0);
|
||||
composePanel.Controls.Add(_btnSend, 1, 0);
|
||||
|
||||
Controls.Add(_rtbHistory);
|
||||
Controls.Add(composePanel);
|
||||
|
||||
_txtCompose.KeyDown += (_, e) =>
|
||||
{
|
||||
if (e.KeyCode == Keys.Enter) { Send(); e.Handled = e.SuppressKeyPress = true; }
|
||||
};
|
||||
_btnSend.Click += (_, _) => Send();
|
||||
}
|
||||
|
||||
// Called by MainForm when a PM message arrives (or we send one and the server echoes it).
|
||||
public void AppendMessage(string time, bool isSelf, string sender, string text)
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
string line = $"[{time}] {sender}: {text}\n";
|
||||
int selStart = _rtbHistory.TextLength;
|
||||
_rtbHistory.AppendText(line);
|
||||
if (isSelf)
|
||||
{
|
||||
_rtbHistory.Select(selStart, line.Length);
|
||||
_rtbHistory.SelectionColor = Color.Gray;
|
||||
_rtbHistory.Select(_rtbHistory.TextLength, 0);
|
||||
_rtbHistory.SelectionColor = _rtbHistory.ForeColor;
|
||||
}
|
||||
_rtbHistory.ScrollToCaret();
|
||||
}
|
||||
|
||||
// Called by MainForm for status events (e.g., the other user disconnected).
|
||||
public void AppendActivity(string text)
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
string line = $"[{DateTime.Now:HH:mm}] {text}\n";
|
||||
int selStart = _rtbHistory.TextLength;
|
||||
_rtbHistory.AppendText(line);
|
||||
_rtbHistory.Select(selStart, line.Length);
|
||||
_rtbHistory.SelectionColor = Color.Gray;
|
||||
_rtbHistory.Select(_rtbHistory.TextLength, 0);
|
||||
_rtbHistory.SelectionColor = _rtbHistory.ForeColor;
|
||||
_rtbHistory.ScrollToCaret();
|
||||
}
|
||||
|
||||
private void Send()
|
||||
{
|
||||
string msg = _txtCompose.Text.Trim();
|
||||
if (string.IsNullOrEmpty(msg)) return;
|
||||
_client.SendText(VcTextScope.Private, _userId, msg);
|
||||
_txtCompose.Clear();
|
||||
// No optimistic echo: the server relays our own message back, so AppendMessage is
|
||||
// called from MainForm.HandleTextMessage when the echo arrives.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user