- 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>
100 lines
2.8 KiB
C#
100 lines
2.8 KiB
C#
using VoiceCat.Interop;
|
|
|
|
namespace VoiceCat.App.Forms;
|
|
|
|
/// <summary>
|
|
/// Simple modal dialog for picking one user from a list of all server users.
|
|
/// Used by "New Private Message" to let the user send a PM to anyone on the server.
|
|
/// </summary>
|
|
public sealed class UserPickerDialog : Form
|
|
{
|
|
private readonly ListBox _lstUsers;
|
|
private readonly Button _btnOk;
|
|
private readonly Button _btnCancel;
|
|
|
|
private readonly List<UserInfo> _users;
|
|
|
|
public uint SelectedUserId { get; private set; }
|
|
|
|
public UserPickerDialog(List<UserInfo> users)
|
|
{
|
|
_users = users;
|
|
|
|
Text = "New Private Message";
|
|
ClientSize = new Size(280, 320);
|
|
MinimumSize = new Size(220, 240);
|
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
|
MaximizeBox = false;
|
|
MinimizeBox = false;
|
|
StartPosition = FormStartPosition.CenterParent;
|
|
|
|
var lbl = new Label
|
|
{
|
|
Text = "Select a user:",
|
|
AutoSize = true,
|
|
Padding = new Padding(6, 6, 6, 2),
|
|
Dock = DockStyle.Top,
|
|
};
|
|
|
|
_lstUsers = new ListBox
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
AccessibleName = "User list",
|
|
};
|
|
foreach (var u in users)
|
|
_lstUsers.Items.Add(new UserItem(u.Id, u.Nickname));
|
|
|
|
var btnPanel = new FlowLayoutPanel
|
|
{
|
|
Dock = DockStyle.Bottom,
|
|
FlowDirection = FlowDirection.RightToLeft,
|
|
Height = 40,
|
|
Padding = new Padding(4),
|
|
AutoSize = false,
|
|
};
|
|
|
|
_btnCancel = new Button
|
|
{
|
|
Text = "Cancel",
|
|
DialogResult = DialogResult.Cancel,
|
|
AutoSize = true,
|
|
};
|
|
_btnOk = new Button
|
|
{
|
|
Text = "OK",
|
|
DialogResult = DialogResult.OK,
|
|
AutoSize = true,
|
|
Enabled = false,
|
|
};
|
|
AcceptButton = _btnOk;
|
|
CancelButton = _btnCancel;
|
|
|
|
btnPanel.Controls.Add(_btnCancel);
|
|
btnPanel.Controls.Add(_btnOk);
|
|
|
|
Controls.Add(_lstUsers);
|
|
Controls.Add(lbl);
|
|
Controls.Add(btnPanel);
|
|
|
|
_lstUsers.SelectedIndexChanged += (_, _) =>
|
|
_btnOk.Enabled = _lstUsers.SelectedItem is UserItem;
|
|
_lstUsers.DoubleClick += (_, _) =>
|
|
{
|
|
if (_lstUsers.SelectedItem is UserItem) DialogResult = DialogResult.OK;
|
|
};
|
|
}
|
|
|
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
|
{
|
|
if (DialogResult == DialogResult.OK && _lstUsers.SelectedItem is UserItem item)
|
|
SelectedUserId = item.UserId;
|
|
base.OnFormClosing(e);
|
|
}
|
|
|
|
private sealed class UserItem(uint userId, string nickname)
|
|
{
|
|
public uint UserId { get; } = userId;
|
|
public override string ToString() => nickname;
|
|
}
|
|
}
|