namespace TinyChat;
///
/// A text input control that allows users to type and send chat messages.
///
public class ChatInputControl : Control, IChatInputControl
{
const string SEND_CHAR = "\u27A4";
const string STOP_CHAR = "\u25A0";
///
/// Occurs before a message is sent from the text box.
///
public event EventHandler? MessageSending;
///
/// The event that is raised when cancellation of a streaming message is requested.
///
public event EventHandler? CancellationRequested;
private readonly TextBox _textBox;
private readonly Button _sendButton;
private bool _isReceivingStream;
///
/// Initializes a new instance of the class.
///
public ChatInputControl()
{
_textBox = new TextBox { Multiline = true, Visible = true, Dock = DockStyle.Fill };
var panel = new Panel { Padding = new Padding(8), Dock = DockStyle.Fill };
Controls.Add(panel);
panel.Controls.Add(_textBox);
var size = new Size(24, 24);
_sendButton = new Button { Text = SEND_CHAR, MaximumSize = size, MinimumSize = size, Anchor = AnchorStyles.Bottom | AnchorStyles.Right };
_sendButton.Left = ClientRectangle.Width - _sendButton.Width - panel.Padding.Right / 2 * 3;
_sendButton.Top = ClientRectangle.Height - _sendButton.Height - panel.Padding.Bottom / 2 * 3;
Controls.Add(_sendButton);
_sendButton.BringToFront();
_sendButton.Click += (s, e) => SendOrStop();
_textBox.KeyPress += TextBox_KeyPress;
}
///
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
_textBox.Focus();
}
///
/// Handles the KeyPress event of the internal text box to send messages on Enter key.
///
/// The source of the event.
/// A that contains the event data.
private void TextBox_KeyPress(object? sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter && !_isReceivingStream) // only send with Enter to prevent unwanted cancellation
{
e.Handled = true;
Send();
}
}
private void SendOrStop()
{
if (_isReceivingStream)
Stop();
else
Send();
}
private void Send()
{
var sendArgs = new MessageSendingEventArgs(null! /* we dont know the sender but the ChatControl does */, new StringMessageContent(_textBox.Text));
MessageSending?.Invoke(this, sendArgs);
if (!sendArgs.Cancel)
_textBox.Clear();
}
private void Stop()
{
if (_sendButton.Enabled)
CancellationRequested?.Invoke(this, EventArgs.Empty);
}
///
void IChatInputControl.SetIsReceivingStream(bool isReceiving, bool allowCancellation)
{
_isReceivingStream = isReceiving;
if (IsAvailable())
{
BeginInvoke(() =>
{
if (IsAvailable())
{
_sendButton.Text = isReceiving && allowCancellation ? STOP_CHAR : SEND_CHAR;
_sendButton.Enabled = !isReceiving || allowCancellation;
}
});
}
}
private bool IsAvailable() => !(Disposing || IsDisposed);
}