Rich Text & Code Editing
The layered text input stack — from a single-line field to a formatting editor and a syntax-highlighting code view.
One Stack, Five Levels
Malena's text input stack is layered. Each level adds one capability, and you pick the level that matches what you need rather than configuring a single do-everything control.
| Class | Builds on | Adds |
|---|---|---|
Typer | Graphic<sf::Text> | Keyboard input on a text object |
TextInput | composes Typer + Cursor | A single-line field with a caret |
TextArea | TextInput | Multiple lines, wrapping, scrolling |
CodeEditor | TextArea | Syntax highlighting |
RichTextEditor | Panel | Formatting toolbar, styled runs, lists |
Typer is rarely used directly — it is the editable layer inside TextInput. Knowing it exists explains where keyboard handling lives when you go looking.RichTextEditor
RichTextEditor is a full editing surface with a formatting toolbar: bold, italic, sizes, colours, alignment and lists.
ml::RichTextEditor notes;
notes.setSize({720.f, 420.f});
notes.setValue("Plain starting text");
notes.onChange([&](const std::string& json){ saveDraft(json); });
It has two content formats, and the distinction matters:
| Method | Format |
|---|---|
setValue(text) | Plain text. Existing formatting is discarded. |
setRichText(json) | The editor's own JSON, preserving every styled run. |
onChange(cb) | Delivers the JSON form on every edit. |
onChange gives you and restore it with setRichText. Round-tripping through setValue silently drops all formatting — the text survives and the styling does not.The toolbar can retract when the editor is not focused, which is worth doing when the editor shares a screen with other controls:
notes.setAutoHideToolbar(true);
CodeEditor
CodeEditor is a TextArea that colours its content.
ml::CodeEditor editor;
editor.setSize({640.f, 400.f});
editor.setLanguage(ml::CodeLanguage::Cpp);
editor.setValue(studentSubmission);
| Language | Value |
|---|---|
| None | CodeLanguage::Plain |
| C++ | CodeLanguage::Cpp |
| Python | CodeLanguage::Python |
| JavaScript | CodeLanguage::JavaScript |
| Java | CodeLanguage::Java |
Because it derives from TextArea, everything a text area does — wrapping, scrolling, selection, placeholder — works here too.
Writing a Highlighter
Highlighting is pluggable. A SyntaxHighlighter turns source text into coloured spans, so a language Malena does not ship is a matter of supplying one.
class LuaHighlighter : public ml::SyntaxHighlighter
{
public:
std::vector<ml::SyntaxToken> tokenize(const std::string& src) override
{
std::vector<ml::SyntaxToken> out;
// append { start, end, colour } for each span
return out;
}
};
editor.setHighlighter(std::make_shared<LuaHighlighter>());
A SyntaxToken is a half-open byte range [start, end) plus a colour. Ranges are byte offsets into the source string.
Call rehighlightCode() after changing content programmatically if the colouring needs to refresh immediately.