Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ProwlEngine/Prowl.Quill/llms.txt

Use this file to discover all available pages before exploring further.

Prowl.Quill’s rich text system lets you embed formatting and animation directives directly in a string using markup tags. The parser produces a QuillRichText object that holds a pre-built glyph layout; drawing it each frame with an updated currentTime value brings animations to life — wavy text, typewriter reveals, colour pulses, and rainbow cycling all update automatically. Because layout is computed once at creation time, drawing is inexpensive and the same object can be safely reused across hundreds of frames.

QuillRichText struct

public struct QuillRichText
{
    /// <summary>Layout size in logical units.</summary>
    public readonly Float2 Size { get; }

    /// <summary>Visible text with all tags stripped (useful for accessibility / clipboard).</summary>
    public readonly string VisibleText { get; }

    /// <summary>Re-anchors animation start time on the next draw — replays typewriter etc.</summary>
    public readonly void Reset();
}
Size is in the same logical units as all other canvas coordinates. VisibleText strips every tag, making it easy to copy text to the clipboard or feed it to a screen reader. Reset() resets the internal animation anchor so effects like typewriter replay from the beginning on the next draw call.

Creating rich text

public QuillRichText CreateRichText(string source, RichTextLayoutSettings settings)
Parses source, performs glyph layout at the current FramebufferScale, and returns a QuillRichText ready to draw. All dimensional fields in settings are specified in logical units; the canvas scales them to physical pixels internally for HiDPI output.
public Float2 MeasureRichText(string source, RichTextLayoutSettings settings)
Convenience wrapper that creates a layout, reads its Size, and discards it. If you also need to draw the text, use CreateRichText + QuillRichText.Size instead to avoid parsing twice.

Drawing rich text

public void DrawRichText(
    QuillRichText text,
    Float2 position,
    double currentTime,
    Float2? origin = null)
Renders the pre-built layout at position (logical units). currentTime is in seconds and drives all time-based effects. The optional origin parameter is a normalised anchor: (0, 0) is the top-left of the text block, (0.5f, 0.5f) centres it, and (1, 1) anchors it at the bottom-right.
currentTime is the heartbeat of every animation effect. Pass DateTime.UtcNow.TimeOfDay.TotalSeconds or your game engine’s accumulated time to keep animations running smoothly. The first draw call after CreateRichText (or after Reset()) anchors the animation start to that currentTime value, so typewriter effects always begin at zero regardless of the absolute time you pass.
public bool GetRichTextLinkAt(
    QuillRichText text,
    Float2 renderOffset,
    Float2 point,
    bool useScissor,
    out string href)
Returns true and sets href when point (in logical units) falls inside a <link> span. Pass the same position you used in DrawRichText as renderOffset. Set useScissor to true to automatically return false for points outside the current scissor region — useful for scrollable panels.

RichTextLayoutSettings

All fields listed below are in logical units unless noted as unitless ratios or speeds.
FieldTypeDescription
RegularFontFontFileDefault body font
BoldFontFontFileFont used by <b>
ItalicFontFontFileFont used by <i>
BoldItalicFontFontFileFont used by <b><i>
MonoFontFontFileFont used by <mono>
FieldTypeDescription
PixelSizefloatBase font size in logical units
LineHeightfloatMultiplier applied to line height
LetterSpacingfloatExtra spacing between glyphs
WordSpacingfloatExtra spacing between words
TabSizeintWidth of a tab stop in spaces
MaxWidthfloatWrap width (0 = no wrapping)
WrapModeWrapModeWord / character wrapping strategy
AlignmentTextAlignmentLeft / Centre / Right
DefaultColorFontColorBase text colour
FieldDescription
DefaultShakeAmpAmplitude of the random per-character displacement (logical units)
DefaultShakeFreqHow many shake updates per second
FieldDescription
DefaultWaveAmpVertical amplitude of the sine wave (logical units)
DefaultWaveFreqFrequency of the wave cycle
DefaultWavePhasePhase offset between adjacent characters
FieldDescription
DefaultRainbowSpeedHow fast hue cycles (unitless)
DefaultRainbowSpreadHue spread across the text block
DefaultRainbowSatHSV saturation (0–1)
DefaultRainbowValueHSV value / brightness (0–1)
FieldDescription
DefaultPulseSpeedAlpha/brightness pulse rate
DefaultPulseAmpPulse amplitude (relative scale, not pixels)
DefaultFadeSpeedFade-in speed
DefaultJitterAmpRandom per-character jitter amplitude (logical units)
DefaultJitterFreqJitter update rate
DefaultTypewriterSpeedCharacters revealed per second
DefaultTypewriterFadeInFade-in duration for each revealed character

Available tags

TagEffect
<b>…</b>Bold text (uses BoldFont)
<i>…</i>Italic text (uses ItalicFont)
<mono>…</mono>Monospace text (uses MonoFont)
<color=#RRGGBB>…</color>Hex colour override
<size=N>…</size>Absolute font size in logical units

Complete example

1

Configure layout settings

Fill in at minimum a RegularFont and PixelSize. All other fields use sensible defaults.
var settings = new RichTextLayoutSettings
{
    RegularFont   = regularFont,
    BoldFont      = boldFont,
    ItalicFont    = italicFont,
    MonoFont      = monoFont,
    PixelSize     = 18f,
    LineHeight    = 1.4f,
    MaxWidth      = 400f,
    DefaultColor  = new FontColor(230, 230, 230, 255),

    // Wave defaults
    DefaultWaveAmp   = 3f,
    DefaultWaveFreq  = 2f,
    DefaultWavePhase = 0.4f,

    // Typewriter defaults
    DefaultTypewriterSpeed  = 20f,
    DefaultTypewriterFadeIn = 0.15f,

    // Rainbow defaults
    DefaultRainbowSpeed  = 1f,
    DefaultRainbowSpread = 0.3f,
    DefaultRainbowSat    = 0.9f,
    DefaultRainbowValue  = 1.0f,
};
2

Create the rich text object

Parse once, keep the result alive across frames.
QuillRichText richText = canvas.CreateRichText(
    "<typewriter>Welcome to <rainbow>Prowl.Quill</rainbow>!\n" +
    "This text uses <b>bold</b>, <i>italic</i>, and <mono>mono</mono> fonts.\n" +
    "<wave>Wavy</wave> and <shake>shaky</shake> effects are <color=#FF8040>time-driven</color>.\n" +
    "Inline <size=24>large</size> and <size=12>small</size> sizes work too.</typewriter>",
    settings);
3

Draw every frame

Pass the accumulated game time so all animations advance correctly.
double currentTime = DateTime.UtcNow.TimeOfDay.TotalSeconds;

// Centre the block on screen
canvas.DrawRichText(
    richText,
    position:    new Float2(canvas.Width / 2f, canvas.Height / 2f),
    currentTime: currentTime,
    origin:      new Float2(0.5f, 0.5f));
4

Handle link clicks (optional)

Call GetRichTextLinkAt in your mouse-click handler.
Float2 mousePos = canvas.PixelToLogical(rawMousePixelPos);

if (canvas.GetRichTextLinkAt(
        richText,
        renderOffset: new Float2(canvas.Width / 2f, canvas.Height / 2f),
        point:        mousePos,
        useScissor:   true,
        out string href))
{
    System.Diagnostics.Process.Start(href);
}
QuillRichText is a value type (struct), but the Layout field it wraps is a reference. Store it as a field or local variable and reuse it every frame — calling CreateRichText on every draw will re-parse and re-layout the text, costing unnecessary CPU time and resetting all animations. Call Reset() when you want animations to replay from the beginning without recreating the object.

Measuring without drawing

Float2 size = canvas.MeasureRichText(source, settings);
Use size to centre a tooltip, size a background panel, or decide whether to scroll. Note that MeasureRichText creates and discards a full layout internally, so if you intend to draw the text too, call CreateRichText instead and read QuillRichText.Size.

Build docs developers (and LLMs) love