Keyboard shortcuts

Press โ† or โ†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Char variants

A char variant is a transformation that is applied to every character of a text at the moment it is written on a surface. It allows you to display a text in a different style (bold, italic, script, circled, superscript, etc.) without having to change the text itself. The styled forms are regular Unicode characters (most of them from the Mathematical Alphanumeric Symbols, Enclosed Alphanumerics and Phonetic Extensions blocks), so they work in any terminal whose font contains those glyphs.

Char variants are described by the CharVariant enum:

#![allow(unused)]
fn main() {
pub enum CharVariant {
    None,                    // default - no conversion
    Uppercase,
    Lowercase,
    MathBold,
    MathItalic,
    MathBoldItalic,
    Script,
    ScriptBold,
    Fraktur,
    FrakturBold,
    DoubleStruck,
    SansSerif,
    SansSerifBold,
    SansSerifItalic,
    SansSerifBoldItalic,
    Monospace,
    Circled,
    CircledNegative,
    Squared,
    SquaredNegative,
    Superscript,
    Subscript,
    SmallCaps,
    Custom(fn(char) -> char),
}
}

Usage

With write_text(...)

The most common way to use a char variant is through the TextFormatBuilder (see Text). The char_variant(...) method sets the variant that will be applied to every character written by write_text(...):

#![allow(unused)]
fn main() {
use appcui::prelude::*;

let mut surface = Surface::new(40, 5);
let format = TextFormatBuilder::new()
    .position(1, 1)
    .attribute(CharAttribute::with_color(Color::Yellow, Color::DarkBlue))
    .align(TextAlignment::Left)
    .char_variant(CharVariant::MathBold)
    .build();
surface.write_text("Hello World 2026", &format);
// the surface now contains: ๐‡๐ž๐ฅ๐ฅ๐จ ๐–๐จ๐ซ๐ฅ๐ ๐Ÿ๐ŸŽ๐Ÿ๐Ÿ”
}

The conversion is done one character at a time and always produces exactly one character, so:

  • the number of characters (and therefore alignment, wrapping and hotkey positions) is the same as for the original text;
  • the hotkey character (if any) is converted as well, but it keeps the hotkey attribute;
  • the original string is never modified - only what is drawn on the surface changes.

The default value is CharVariant::None, meaning that the text is written as it is.

With map(...)

A CharVariant can also be used directly, to convert a single character through the map(...) method:

#![allow(unused)]
fn main() {
pub fn map(&self, ch: char) -> char
}

This is useful if you want to build a converted string yourself (for example to use it with write_string(...) or to store it):

#![allow(unused)]
fn main() {
use appcui::prelude::*;

let v = CharVariant::Circled;
assert_eq!(v.map('A'), 'โ’ถ');
assert_eq!(v.map('7'), 'โ‘ฆ');
assert_eq!(v.map('!'), '!'); // characters without a variant are left unchanged

let text: String = "Hello".chars().map(|c| CharVariant::DoubleStruck.map(c)).collect();
assert_eq!(text, "โ„๐•–๐•๐•๐• ");
}

Custom variants

If none of the predefined variants fits your needs, CharVariant::Custom accepts any function with the signature fn(char) -> char:

#![allow(unused)]
fn main() {
use appcui::prelude::*;

fn leet(c: char) -> char {
    match c {
        'o' | 'O' => '0',
        'l' | 'L' => '1',
        'e' | 'E' => '3',
        _ => c,
    }
}

let format = TextFormatBuilder::new()
    .position(0, 0)
    .char_variant(CharVariant::Custom(leet))
    .build();
surface.write_text("Hello World", &format); // writes: H3110 W0r1d
}

Translation rules

The following table describes how each variant translates characters. Any character that is not listed in the Translation column (punctuation, spaces, accented letters, other Unicode characters, etc.) is left unchanged.

VariantTranslationNotes
None-No conversion (default).
Uppercaseany letter โ†’ its upper case formUses Rustโ€™s Unicode case mapping (works for non-ASCII letters as well, e.g. ฤƒ โ†’ ฤ‚). Only the first character of the mapping is kept.
Lowercaseany letter โ†’ its lower case formSame as Uppercase, but for lower case.
MathBoldA-Z โ†’ ๐€-๐™, a-z โ†’ ๐š-๐ณ, 0-9 โ†’ ๐ŸŽ-๐Ÿ—
MathItalicA-Z โ†’ ๐ด-๐‘, a-z โ†’ ๐‘Ž-๐‘งh โ†’ โ„Ž (Planck constant), because the regular italic h slot is not assigned in Unicode. Digits are not converted.
MathBoldItalicA-Z โ†’ ๐‘จ-๐’, a-z โ†’ ๐’‚-๐’›, 0-9 โ†’ ๐ŸŽ-๐Ÿ—There are no bold-italic digits, so digits use the bold form.
ScriptA-Z โ†’ ๐’œ-๐’ต, a-z โ†’ ๐’ถ-๐“B E F H I L M R โ†’ โ„ฌ โ„ฐ โ„ฑ โ„‹ โ„ โ„’ โ„ณ โ„› and e g o โ†’ โ„ฏ โ„Š โ„ด (taken from the Letterlike Symbols block). Digits are not converted.
ScriptBoldA-Z โ†’ ๐“-๐“ฉ, a-z โ†’ ๐“ช-๐”ƒ, 0-9 โ†’ ๐ŸŽ-๐Ÿ—Digits use the bold form.
FrakturA-Z โ†’ ๐”„-โ„จ, a-z โ†’ ๐”ž-๐”ทC H I R Z โ†’ โ„ญ โ„Œ โ„‘ โ„œ โ„จ (taken from the Letterlike Symbols block). Digits are not converted.
FrakturBoldA-Z โ†’ ๐•ฌ-๐–…, a-z โ†’ ๐–†-๐–Ÿ, 0-9 โ†’ ๐ŸŽ-๐Ÿ—Digits use the bold form.
DoubleStruckA-Z โ†’ ๐”ธ-โ„ค, a-z โ†’ ๐•’-๐•ซ, 0-9 โ†’ ๐Ÿ˜-๐ŸกC H N P Q R Z โ†’ โ„‚ โ„ โ„• โ„™ โ„š โ„ โ„ค (taken from the Letterlike Symbols block).
SansSerifA-Z โ†’ ๐– -๐–น, a-z โ†’ ๐–บ-๐—“, 0-9 โ†’ ๐Ÿข-๐Ÿซ
SansSerifBoldA-Z โ†’ ๐—”-๐—ญ, a-z โ†’ ๐—ฎ-๐˜‡, 0-9 โ†’ ๐Ÿฌ-๐Ÿต
SansSerifItalicA-Z โ†’ ๐˜ˆ-๐˜ก, a-z โ†’ ๐˜ข-๐˜ป, 0-9 โ†’ ๐Ÿข-๐ŸซThere are no sans-serif italic digits, so digits use the sans-serif form.
SansSerifBoldItalicA-Z โ†’ ๐˜ผ-๐™•, a-z โ†’ ๐™–-๐™ฏ, 0-9 โ†’ ๐Ÿฌ-๐ŸตDigits use the sans-serif bold form.
MonospaceA-Z โ†’ ๐™ฐ-๐š‰, a-z โ†’ ๐šŠ-๐šฃ, 0-9 โ†’ ๐Ÿถ-๐Ÿฟ
CircledA-Z โ†’ โ’ถ-โ“, a-z โ†’ โ“-โ“ฉ, 1-9 โ†’ โ‘ -โ‘จ, 0 โ†’ โ“ช
CircledNegativeA-Z โ†’ ๐Ÿ…-๐Ÿ…ฉ, a-z โ†’ ๐Ÿ…-๐Ÿ…ฉ, 1-9 โ†’ โถ-โพ, 0 โ†’ โ“ฟOnly upper case letters exist, so lower case letters are converted to their upper case form first.
SquaredA-Z โ†’ ๐Ÿ„ฐ-๐Ÿ…‰, a-z โ†’ ๐Ÿ„ฐ-๐Ÿ…‰Only upper case letters exist (lower case is converted to upper case first). Digits are not converted.
SquaredNegativeA-Z โ†’ ๐Ÿ…ฐ-๐Ÿ†‰, a-z โ†’ ๐Ÿ…ฐ-๐Ÿ†‰Only upper case letters exist (lower case is converted to upper case first). Digits are not converted. Some fonts render ๐Ÿ…ฐ ๐Ÿ…ฑ ๐Ÿ…พ ๐Ÿ…ฟ as emoji (two cells wide).
SuperscriptA-Z โ†’ แดฌ แดฎ แถœ แดฐ แดฑ แถ  แดณ แดด แดต แดถ แดท แดธ แดน แดบ แดผ แดพ Q แดฟ หข แต€ แต โฑฝ แต‚ หฃ สธ แถป
a-z โ†’ แตƒ แต‡ แถœ แตˆ แต‰ แถ  แต สฐ โฑ สฒ แต หก แต โฟ แต’ แต– q สณ หข แต— แต˜ แต› สท หฃ สธ แถป
0-9 โ†’ โฐ-โน, + - = ( ) โ†’ โบ โป โผ โฝ โพ
C F S X Y Z have no superscript capital, so the lower case superscript form is used. Q and q have no superscript form and are left unchanged.
Subscripta e h i j k l m n o p r s t u v x (and their upper case forms) โ†’ โ‚ โ‚‘ โ‚• แตข โฑผ โ‚– โ‚— โ‚˜ โ‚™ โ‚’ โ‚š แตฃ โ‚› โ‚œ แตค แตฅ โ‚“
0-9 โ†’ โ‚€-โ‚‰, + - = ( ) โ†’ โ‚Š โ‚‹ โ‚Œ โ‚ โ‚Ž
Unicode has no upper case subscript letters and only some lower case ones, so upper case is mapped to the lower case subscript and b c d f g q w y z are left unchanged.
SmallCapsa-z (and their upper case forms) โ†’ แด€ ส™ แด„ แด… แด‡ ๊œฐ ษข สœ ษช แดŠ แด‹ สŸ แด ษด แด แด˜ ๊žฏ ส€ ๊œฑ แด› แดœ แด  แดก x ส แดขThere is no small capital x, so x/X are left unchanged. Currently T (upper case) is not converted - only t is.
Custom(f)every character c โ†’ f(c)The function is called for every character that is written.

Examples

The following table shows how the text Hello World 2026 is displayed with each variant:

VariantResult
NoneHello World 2026
UppercaseHELLO WORLD 2026
Lowercasehello world 2026
MathBold๐‡๐ž๐ฅ๐ฅ๐จ ๐–๐จ๐ซ๐ฅ๐ ๐Ÿ๐ŸŽ๐Ÿ๐Ÿ”
MathItalic๐ป๐‘’๐‘™๐‘™๐‘œ ๐‘Š๐‘œ๐‘Ÿ๐‘™๐‘‘ 2026
MathBoldItalic๐‘ฏ๐’†๐’๐’๐’ ๐‘พ๐’๐’“๐’๐’… ๐Ÿ๐ŸŽ๐Ÿ๐Ÿ”
Scriptโ„‹โ„ฏ๐“๐“โ„ด ๐’ฒโ„ด๐“‡๐“๐’น 2026
ScriptBold๐“—๐“ฎ๐“ต๐“ต๐“ธ ๐“ฆ๐“ธ๐“ป๐“ต๐“ญ ๐Ÿ๐ŸŽ๐Ÿ๐Ÿ”
Frakturโ„Œ๐”ข๐”ฉ๐”ฉ๐”ฌ ๐”š๐”ฌ๐”ฏ๐”ฉ๐”ก 2026
FrakturBold๐•ณ๐–Š๐–‘๐–‘๐–” ๐–‚๐–”๐–—๐–‘๐–‰ ๐Ÿ๐ŸŽ๐Ÿ๐Ÿ”
DoubleStruckโ„๐•–๐•๐•๐•  ๐•Ž๐• ๐•ฃ๐•๐•• ๐Ÿš๐Ÿ˜๐Ÿš๐Ÿž
SansSerif๐–ง๐–พ๐—…๐—…๐—ˆ ๐–ถ๐—ˆ๐—‹๐—…๐–ฝ ๐Ÿค๐Ÿข๐Ÿค๐Ÿจ
SansSerifBold๐—›๐—ฒ๐—น๐—น๐—ผ ๐—ช๐—ผ๐—ฟ๐—น๐—ฑ ๐Ÿฎ๐Ÿฌ๐Ÿฎ๐Ÿฒ
SansSerifItalic๐˜๐˜ฆ๐˜ญ๐˜ญ๐˜ฐ ๐˜ž๐˜ฐ๐˜ณ๐˜ญ๐˜ฅ ๐Ÿค๐Ÿข๐Ÿค๐Ÿจ
SansSerifBoldItalic๐™ƒ๐™š๐™ก๐™ก๐™ค ๐™’๐™ค๐™ง๐™ก๐™™ ๐Ÿฎ๐Ÿฌ๐Ÿฎ๐Ÿฒ
Monospace๐™ท๐šŽ๐š•๐š•๐š˜ ๐š†๐š˜๐š›๐š•๐š ๐Ÿธ๐Ÿถ๐Ÿธ๐Ÿผ
Circledโ’ฝโ“”โ“›โ“›โ“ž โ“Œโ“žโ“กโ“›โ““ โ‘กโ“ชโ‘กโ‘ฅ
CircledNegative๐Ÿ…—๐Ÿ…”๐Ÿ…›๐Ÿ…›๐Ÿ…ž ๐Ÿ…ฆ๐Ÿ…ž๐Ÿ…ก๐Ÿ…›๐Ÿ…“ โทโ“ฟโทโป
Squared๐Ÿ„ท๐Ÿ„ด๐Ÿ„ป๐Ÿ„ป๐Ÿ„พ ๐Ÿ…†๐Ÿ„พ๐Ÿ…๐Ÿ„ป๐Ÿ„ณ 2026
SquaredNegative๐Ÿ…ท๐Ÿ…ด๐Ÿ…ป๐Ÿ…ป๐Ÿ…พ ๐Ÿ††๐Ÿ…พ๐Ÿ†๐Ÿ…ป๐Ÿ…ณ 2026
Superscriptแดดแต‰หกหกแต’ แต‚แต’สณหกแตˆ ยฒโฐยฒโถ
Subscriptโ‚•โ‚‘โ‚—โ‚—โ‚’ Wโ‚’แตฃโ‚—d โ‚‚โ‚€โ‚‚โ‚†
SmallCapsสœแด‡สŸสŸแด แดกแดส€สŸแด… 2026

Writing the same text with multiple variants

The following example writes Hello World 2026 on several lines of a surface, each one using a different variant:

#![allow(unused)]
fn main() {
use appcui::prelude::*;

let variants = [
    ("None", CharVariant::None),
    ("MathBold", CharVariant::MathBold),
    ("Script", CharVariant::Script),
    ("Fraktur", CharVariant::Fraktur),
    ("DoubleStruck", CharVariant::DoubleStruck),
    ("Circled", CharVariant::Circled),
    ("Superscript", CharVariant::Superscript),
    ("SmallCaps", CharVariant::SmallCaps),
];
let mut surface = Surface::new(40, variants.len() as u32);
for (index, (name, variant)) in variants.iter().enumerate() {
    // the name of the variant (written as it is)
    surface.write_string(0, index as i32, name, CharAttribute::with_fore_color(Color::Gray), false);
    // the text, converted with the current variant
    let format = TextFormatBuilder::new()
        .position(16, index as i32)
        .attribute(CharAttribute::with_color(Color::Yellow, Color::DarkBlue))
        .char_variant(*variant)
        .build();
    surface.write_text("Hello World 2026", &format);
}
}

The result will look like this:

None            Hello World 2026
MathBold        ๐‡๐ž๐ฅ๐ฅ๐จ ๐–๐จ๐ซ๐ฅ๐ ๐Ÿ๐ŸŽ๐Ÿ๐Ÿ”
Script          โ„‹โ„ฏ๐“๐“โ„ด ๐’ฒโ„ด๐“‡๐“๐’น 2026
Fraktur         โ„Œ๐”ข๐”ฉ๐”ฉ๐”ฌ ๐”š๐”ฌ๐”ฏ๐”ฉ๐”ก 2026
DoubleStruck    โ„๐•–๐•๐•๐•  ๐•Ž๐• ๐•ฃ๐•๐•• ๐Ÿš๐Ÿ˜๐Ÿš๐Ÿž
Circled         โ’ฝโ“”โ“›โ“›โ“ž โ“Œโ“žโ“กโ“›โ““ โ‘กโ“ชโ‘กโ‘ฅ
Superscript     แดดแต‰หกหกแต’ แต‚แต’สณหกแตˆ ยฒโฐยฒโถ
SmallCaps       สœแด‡สŸสŸแด แดกแดส€สŸแด… 2026

Remarks

  • Char variants are purely visual: the converted characters are different Unicode code points, so text written this way should not be used for copy/paste, search or accessibility purposes.
  • Font support differs between terminals. The Mathematical Alphanumeric Symbols (MathBold, Script, Fraktur, DoubleStruck, SansSerif*, Monospace) are widely supported, while some of the Enclosed Alphanumeric Supplement characters (CircledNegative, Squared, SquaredNegative) may be missing or rendered as wide emoji.
  • Because the translation is done per character, variants that do not have a form for a specific character (for example Subscript for b, or Squared for digits) simply leave that character as it is.