RichTextLabel: BBCode, user text and effects
A complete C# example: literal text, an allowed link, progressive reveal and a custom RichTextEffect.
Run the example
Add UiSampleStyle.cs, RichTextDemo.cs and BlueGlowEffect.cs. Attach RichTextDemo to a Control root, build and run with F6. The link changes local status, the slider reveals part of the text, the name retains entered tags literally and the formatted lower line uses a custom effect.
RichTextDemo.cs
using Reckfell;
public partial class RichTextDemo : Control
{
public RichTextLabel Info { get; private set; } = null!;
public RichTextLabel Log { get; private set; } = null!;
public Label Status { get; private set; } = null!;
public int LinkCount { get; private set; }
public BlueGlowEffect Effect { get; private set; } = null!;
public override void _Ready()
{
UiSampleStyle.Prepare(this);
var margin = new MarginContainer(); AddChild(margin); margin.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect, margin: 24);
var column = new VBoxContainer(); margin.AddChild(column);
Info = new RichTextLabel
{
BbcodeEnabled = true, FitContent = true, ScrollActive = false, Threaded = false,
Text = "[url=docs]" + UiSampleStyle.Text("Документация", "Documentation") + "[/url]\n[b]Reckfell[/b] [color=#668fff]C# UI[/color]",
VisibleCharactersBehavior = TextServer.VisibleCharactersBehavior.CharsAfterShaping
};
column.AddChild(Info);
Effect = new BlueGlowEffect(); Info.InstallEffect(Effect);
Info.AppendText("\n[blueglow speed=2]" + UiSampleStyle.Text("Свой эффект", "Custom effect") + "[/blueglow]");
var reveal = new HSlider { MinValue = 0, MaxValue = 1, Step = .01, Value = 1 }; column.AddChild(reveal);
reveal.ValueChanged += value => SetReveal((float)value);
Log = new RichTextLabel { BbcodeEnabled = true, SizeFlagsVertical = SizeFlags.ExpandFill, ScrollFollowing = true, Threaded = false }; column.AddChild(Log);
Status = new Label { Text = UiSampleStyle.Text("Ссылка обрабатывается внутри примера", "The example handles its link locally") }; column.AddChild(Status);
Info.MetaClicked += HandleMeta;
AddName("[b]Player[/b]");
}
public void AddName(string text)
{
Log.PushColor(new Color(.4f, .6f, 1)); Log.AddText(UiSampleStyle.Text("Имя: ", "Name: ")); Log.Pop();
Log.AddText(text); Log.Newline();
}
public void SetReveal(float ratio) { if (float.IsFinite(ratio)) Info.VisibleRatio = Mathf.Clamp(ratio, 0, 1); }
private void HandleMeta(Variant data)
{
if (data.VariantType != Variant.Type.String || data.AsString() != "docs") return;
LinkCount++; Status.Text = UiSampleStyle.Text("Выбрана документация", "Documentation selected");
}
}BlueGlowEffect.cs
using Reckfell;
[Tool]
public partial class BlueGlowEffect : RichTextEffect
{
[Export] public string bbcode { get; set; } = "blueglow";
public int Calls { get; private set; }
public override bool _ProcessCustomFX(CharFXTransform charFX)
{
Calls++;
double speed = 2;
if (charFX.Env.TryGetValue("speed", out Variant value) &&
(value.VariantType == Variant.Type.Float || value.VariantType == Variant.Type.Int))
{
double requested = value.AsDouble();
if (double.IsFinite(requested)) speed = System.Math.Clamp(requested, 0, 10);
}
Color color = charFX.Color;
color.A *= (float)(.75 + .25 * System.Math.Cos(charFX.ElapsedTime * speed));
color.R *= .5f; color.G *= .7f; charFX.Color = color;
return true;
}
}Three ways to append
| Method | Behavior |
|---|---|
| Text with BbcodeEnabled | Replaces content and parses markup |
| AppendText | Appends BBCode markup |
| AddText | Appends ordinary text without parsing tags |
| PushColor/PushFont/PushMeta and Pop | Establish context for subsequent additions; close each push |
Pass player names/messages through AddText. Concatenating unchecked text into BBCode lets it alter formatting and link metadata. If external values must be embedded in markup, escape according to BBCode rules; HTML escaping is not a substitute. GetParsedText returns text without parsed tags from an already built document; it is not a universal arbitrary-BBCode validator/filter.
A link must not automatically execute an arbitrary command or URL. MetaClicked supplies a Variant; the example accepts only the string docs. Define allowed destinations/schemes and the intended action for external navigation.
Compact markup reference
| Purpose | Main tags / options |
|---|---|
| Style | b, i, u, s, code; appropriate fonts are needed |
| Color | color/bgcolor/fgcolor; named color or #RRGGBB/#RRGGBBAA |
| Font and size | font, font_size; use an existing resource and reasonable size |
| Paragraph | p, center, left, right, fill, indent; check direction/language/spacing on real text |
| Lists | ul/ol; numbering type and bullet configure presentation |
| Link / tooltip | url, hint; your handler defines link behavior |
| Image | img; path, dimensions and vertical alignment; do not assume automatic network-address loading |
| Table | table with column count, cell; borders, padding, backgrounds and cell bounds |
| Horizontal rule | hr with thickness/color/width options |
| Literal brackets | lb/rb inside trusted BBCode |
| Built-in animation | pulse, wave, tornado, shake, fade, rainbow; frequency/amplitude/color options depend on the effect |
Nest/close tags correctly and apply paragraph settings at paragraph level. A color tag does not establish readable contrast; wave does not change layout. See RichTextLabel API for full parameters and matching Push methods.
Reveal, scrolling and cost
VisibleRatio 0…1 controls a character fraction; 1 removes the limit. This is neither BBCode string length nor .NET string.Length. The example uses CharsAfterShaping; truncating before shaping can change wrapping/ligatures. A typewriter needs attention to graphemes, translation and script; glyph counts differ from Unicode-character counts.
FitContent sizes to content; ScrollActive enables internal scrolling. Choose a bounded region or external scrolling and check container dimensions. ScrollFollowing suits a log. Threaded allows background preparation, but completion/measurements need IsFinished/Finished; the example disables it for a small predictable document.
Avoid rebuilding a large Text through += every frame, which can reparse/reshape it. Append in batches, limit log size and profile tables, images, fallback fonts and effects. Each push/pop should leave the intended stack for the next batch.
A custom effect
BlueGlowEffect registers its name through bbcode; lowercase is the engine lookup contract. InstallEffect runs before the tag is added. Env contains parsed arguments; validate type/range. ElapsedTime is effect time, not delta. GlyphIndex is a font glyph index, not a Unicode code point; mapping to source text requires Range/shaping data. Avoid file I/O or expensive per-glyph work. Tool enables editor use; running the game example is checked separately.