Strings and Chars
The string classes describe terminal text before it is rendered. They let you represent a single display character with color and character attributes, build styled text fragments, measure Unicode-aware width, and split or wrap text without interacting with a buffer.
This page focuses on working with Block, BlockStyle, BlockString and BlockStringEditor as data types.
For rendering text blocks into rectangles with BlockText, see BlockText Rendering.
Usage
Working with Block
Block represents a single terminal display cell together with its foreground color, background color, and optional
character attributes such as bold or underline.
Constructing Characters ^^^^^^^^^^^^^^^^^^^^^^^
Use a Unicode code point when you construct a regular character. This is the most explicit form when you already know the exact character.
const auto checkMark = Block{U'✓'};
const auto whiteOnBlue = Block{U'a', fg::White, bg::Blue};
For combined characters, construct Block from UTF-8 or UTF-32 text:
const auto combinedFromUtf8 = Block{"e\xCC\x81"};
const auto combinedFromUtf32 = Block{U"e\u0301"};
To append a combining mark programmatically, use withCombining():
const auto acute = Block{U"e"}.withCombining(char32_t{0x0301});
displayWidth() follows terminal cell width, so wide and combining characters behave correctly during layout:
const auto wide = Block{U'界'};
const auto combined = Block{"e\xCC\x81"};
const auto wideCells = wide.displayWidth(); // 2
const auto combinedCells = combined.displayWidth(); // 1
Testing and Comparing Characters ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To compare only the character value and ignore color, compare against a Unicode code point:
if (character == U'?') {
// ...
}
Use regular comparisons for small sets of accepted characters:
if (character == U'Y' || character == U'y' || character == U'J' || character == U'j') {
// ...
}
For screen-level comparisons, renderedEquals() is often more useful than operator== because it treats inherited
colors like terminal defaults:
const auto inherited = Block{U'X', fg::Inherited, bg::Inherited};
const auto defaults = Block{U'X', fg::Default, bg::Default};
const auto sameOnScreen = inherited.renderedEquals(defaults);
Recoloring Characters ^^^^^^^^^^^^^^^^^^^^^
To adjust the colors of an existing Block, choose the method that matches your intent:
const auto base = Block{U'X', fg::Green, bg::Blue};
const auto overlay = base.withOverlay(BlockStyle{Color{fg::BrightWhite, bg::Inherited}});
const auto replaced = base.withColorReplaced(Color{fg::White, bg::Black});
const auto basedOnTheme = base.withBase(BlockStyle{Color{fg::Inherited, bg::BrightBlack}});
With withOverlay(), any Inherited component keeps the existing color, while Default resets that component to
the terminal default.
Working with Character Attributes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use BlockAttributes when you want a character or string fragment to explicitly enable, disable, or inherit ANSI text
attributes.
auto emphasis = BlockAttributes{};
emphasis.setBold(true);
emphasis.setUnderline(true);
const auto heading = Block{U'H', fg::BrightWhite, emphasis};
const auto plain = heading.withAttributes(BlockAttributes::reset());
Unspecified attributes inherit from the surrounding writer or string context, while specified bits overwrite that base state.
Working with Character Styles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use BlockStyle when you want to bundle color and character attributes into one reusable value.
auto emphasis = BlockAttributes{};
emphasis.setBold(true);
const auto headingStyle = BlockStyle{Color{fg::BrightWhite, bg::Blue}, emphasis};
auto heading = Block{U'H', headingStyle};
heading.setStyle(heading.style().withOverlay(BlockStyle{Color{fg::Inherited, bg::Black}}));
withOverlay() keeps inherited color components and unspecified attributes from the existing style, while
withBase() resolves a style against an underlying base theme.
Block Strings
BlockString is the primary owning read-only sequence of Block values.
Use it for status bars, prompts, labels, stored options, drawing inputs and completed lines.
BlockStringEditor is the mutable counterpart used while a sequence is actively assembled or changed.
Building Colored BlockText Fragments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Prefer append(...) when building mixed-style strings.
It keeps colors, attributes, and text in a single readable sequence.
auto footer = BlockStringEditor{};
footer.append(
bg::BrightBlack,
fg::BrightYellow,
"[Q]",
fg::BrightWhite,
" quit");
auto highlighted = BlockAttributes{};
highlighted.setBold(true);
highlighted.setUnderline(true);
footer.append(
" ",
highlighted,
"Save",
BlockAttributes::reset(),
fg::BrightBlack,
" shortcut");
append(...) accepts colors, BlockStyle, Block values, BlockAttributes, plain text and other block
strings.
Colors and attributes remain active for subsequent elements within the same call.
Completed text can be constructed directly as a BlockString from UTF-8 or UTF-32 text:
const auto utf8Text = BlockString{"Gruezi"};
const auto utf32Text = BlockString{U"Gru\u0308ezi"};
Control codes are filtered out automatically, except for tab and newline, which are preserved for layout and splitting.
Implicit Sharing and Ranges
BlockStringEditor is implicitly shared.
Copying a string is cheap because the character storage is shared until one copy is modified through a mutable API.
Once that happens, the modified instance detaches and continues with its own private copy of the visible character
range.
This keeps normal value semantics:
auto original = BlockStringEditor{"alpha"};
auto copy = original; // shared storage
copy[0] = Block{U'A'}; // detaches here
// original is still "alpha"
// copy is now "Alpha"
BlockString owns or shares its storage and exposes only non-mutating operations.
A BlockStringEditor converts implicitly to BlockString, so read-only APIs safely accept both categories.
void drawLabel(BlockString text);
auto title = BlockStringEditor{"Status: ready"};
drawLabel(title); // implicit BlockStringEditor -> BlockString
Handling Invalid UTF-8 Input
UTF-8-based constructors are tolerant and replace malformed input.
const auto text = el::String{"Gr\xC3"};
const auto displayText = BlockString{text}; // inserts U+FFFD
Validate byte or standard-string input before constructing Erbsland Core text when invalid data should fail fast.
For existing UTF-8 Erbsland Core strings, call isValidUtf8() explicitly when validation is required.
Searching, Slicing, and Measuring
Use size() to count stored characters and displayWidth() to measure terminal cell width.
These differ for full-width and combining characters.
const auto text = BlockString{"A界e\xCC\x81"};
const auto characterCount = text.size(); // 3 terminal characters
const auto terminalWidth = text.displayWidth(); // 4 cells
For content-aware processing, the following helpers are commonly useful:
const auto warningCount = BlockString{"!?!!"}.count(U'!');
const auto nextQuestion = BlockString{"abc?def"}.indexOf(U'?');
const auto middle = BlockString{"AB界D"}.substr(1, 2);
count(Block) and indexOf(Block) compare both character and color.
The char32_t overloads ignore color and match only the character.
Splitting, Wrapping, and Lines
splitWords() separates text at spaces, tabs, carriage returns, and newlines.
splitLines() keeps empty lines and splits only at newline characters.
const auto words = BlockString{"alpha beta\ngamma"}.splitWords();
const auto lines = BlockString{"first\n\nthird"}.splitLines();
When you want string-level wrapping without creating a BlockText object, use wrapIntoLines():
const auto wrapped = BlockString{"alpha beta\ngamma"}.wrapIntoLines(6);
const auto wrappedWithSpacing = BlockString{"alpha beta\ngamma"}.wrapIntoLines(
6,
ParagraphSpacing::DoubleLine);
const auto lineCount = BlockString{"AA\nBB"}.terminalLines(2);
This is useful for preprocessing text, estimating layout, or building buffers line by line.
BlockStringLines is the line-based companion type returned by splitLines() and wrapIntoLines().
You can also rebuild a string from line data:
const auto text = BlockString::fromLines({"one", "two"}, Color{fg::BrightWhite, bg::Blue});
If you want to render these lines directly into a buffer, see Buffer::fromLines() and BlockText Rendering.