using VoiceCat.Interop; namespace VoiceCat.App.Forms; /// /// Modeless window for a single private message conversation. /// Created by MainForm and shown non-modally; MainForm routes incoming PMs here. /// 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. } }