ml::docs | Your First App Manifests Events Messaging Plugins Scenes Resources
See all tutorials →
Beginner 3 sections ~8 min C++17/20

Chat

A scrolling transcript with an input row, and the small message struct that travels between machines unchanged.

Introduction

Two Classes

Two classes provide a chat surface. ChatWindow is the whole thing — a scrolling transcript with an input row. ChatBubble renders one message and is managed for you.

ml::ChatWindow chat;
chat.setSize({360.f, 520.f});
chat.onSend([&](const std::string& text){ sendToServer(text); });
You do not normally construct ChatBubble yourself. ChatWindow builds one per message and lays them out — the class is public so you can render a transcript somewhere else, not because you need it for a chat window.
1Data

The Message Struct

A message is a small plain struct, so it travels over the network or into a database without conversion.

struct ChatMessage {
    std::string sender;
    std::string text;
    std::string timestamp;
    bool        isMine = false;
};
FieldMeaning
senderDisplay name shown above the bubble.
textThe message body. Wrapped by the bubble.
timestampDisplay time. ChatMessage::isoToHM() converts an ISO string, or returns the current time when given nothing.
isMineWhich side to align to, and which colour to use.
ml::ChatMessage m;
m.sender    = "Amelia";
m.text      = "Is the quiz open yet?";
m.timestamp = ml::ChatMessage::isoToHM();     // now, as HH:MM
m.isMine    = false;
chat.addMessage(m);
isMine is presentation, not identity. Set it by comparing the sender to the local user when the message arrives — a transcript replayed on another machine needs the flag recomputed, not the stored value.
2Wiring

ChatWindow

MethodEffect
addMessage(msg)Append and scroll to it.
clear()Empty the transcript.
onSend(cb)The user submitted the input row.
setSize(size)Resize; bubbles re-wrap to the new width.

onSend gives you the text and nothing else. The window does not add the message itself — that is deliberate, so you can send it, wait for the server to accept it, and add it only if that succeeded:

chat.onSend([&](const std::string& text){
    if (!socket.isOpen()) { showToast("Not connected"); return; }
    socket.send(text);
    ml::ChatMessage mine{ myName, text, ml::ChatMessage::isoToHM(), true };
    chat.addMessage(mine);          // echo locally
});
If you echo locally and the server broadcasts back to everyone including the sender, the message appears twice. Either skip the local echo or have the server exclude the sender — decide which before wiring the transport.