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

Rich Text & Code Editing

The layered text input stack — from a single-line field to a formatting editor and a syntax-highlighting code view.

Introduction

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.

ClassBuilds onAdds
TyperGraphic<sf::Text>Keyboard input on a text object
TextInputcomposes Typer + CursorA single-line field with a caret
TextAreaTextInputMultiple lines, wrapping, scrolling
CodeEditorTextAreaSyntax highlighting
RichTextEditorPanelFormatting 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.
1Formatting

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:

MethodFormat
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.
Persist what 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);
2Code

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);
LanguageValue
NoneCodeLanguage::Plain
C++CodeLanguage::Cpp
PythonCodeLanguage::Python
JavaScriptCodeLanguage::JavaScript
JavaCodeLanguage::Java

Because it derives from TextArea, everything a text area does — wrapping, scrolling, selection, placeholder — works here too.

3Extending

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.

Byte offsets, not character offsets. If your language allows non-ASCII identifiers or strings, make sure a token boundary never lands inside a multi-byte UTF-8 sequence — a split character cannot be rendered.

Call rehighlightCode() after changing content programmatically if the colouring needs to refresh immediately.