Themes

The erbsland::cterm::theme namespace provides the theme system used by the UI framework. A theme keeps visual decisions out of individual controls, so you can change the look of an application without teaching every scroll bar, panel, or status line about the same colors, block shapes, margins, and padding again.

The public include for this subsystem is:

#include <erbsland/cterm/theme/all.hpp>

The UI umbrella include also makes the theme types available for normal UI applications:

#include <erbsland/cterm/ui/all.hpp>

Core Model

A theme is an immutable hierarchy of property sheets:

  • ThemeBuilder is the mutable authoring API. It stores one Properties object per Selector.

  • Theme freezes those authored definitions and resolves fallback chains on demand.

  • PropertySheet is the effective result for one selector. It contains the resolved color, color sequence, attributes, block table, margins, and padding.

This model lets you change text styling, terminal-cell graphics, and layout spacing through the same selector tree. You can override only the value you care about and inherit the remaining properties from the nearest base sheet.

The default application theme is Theme::dark(). You can replace it on the application, or override it on a page:

using namespace erbsland::cterm;

auto app = ui::Application{};
app.setTheme(theme::Theme::light());

auto page = ui::Page::create();
page->setTheme(theme::Theme::monochrome());

For small customizations, start with a builder copied from an existing theme and edit only the affected selectors:

auto builder = theme::ThemeBuilder::from(theme::Theme::dark());
builder.edit(theme::Selector{theme::Element::StatusLine, theme::Part::Background})
    .setColor(Color{fg::BrightWhite, bg::Blue});

app.setTheme(builder.build());

Selectors

Theme lookup uses compact identifiers instead of strings in the paint loop. A selector has four parts:

  • Element describes the themed control or surface.

  • Part describes a visual role inside the element.

  • States describes required UI states such as focus or disabled.

  • Tags describes optional, application-defined variants.

The built-in elements are static constants on Element:

theme::Element::Base
theme::Element::Page
theme::Element::Layout
theme::Element::Surface
theme::Element::Panel
theme::Element::TextBox
theme::Element::StatusLine
theme::Element::HeaderLine
theme::Element::FooterLine
theme::Element::ActionHelp
theme::Element::HorizontalScrollBar
theme::Element::VerticalScrollBar
theme::Element::ScrollCorner
theme::Element::Sections
theme::Element::Buttons
theme::Element::Button
theme::Element::Frame
theme::Element::Choice
theme::Element::HelpViewer

The built-in parts are static constants on Part:

theme::Part::Background
theme::Part::Text
theme::Part::Border
theme::Part::Track
theme::Part::Thumb
theme::Part::Decrease
theme::Part::Increase
theme::Part::Indicator
theme::Part::Key
theme::Part::KeyBracket
theme::Part::Title
theme::Part::TitleBracket
theme::Part::Spacing

The built-in states are static constants on State:

theme::State::Focused
theme::State::FocusWithin
theme::State::Selected
theme::State::Disabled
theme::State::Checked

You can create custom elements and parts for your own controls:

inline constexpr auto cSearchBox = theme::Element::custom(10'000);
inline constexpr auto cCursor = theme::Part::custom(10'001);

Keep these identifiers stable in your application or library. They are small numeric values by design, so lookup stays direct and cheap. Custom element and part identifiers are offset by the library, so they do not collide with predefined identifiers.

Custom elements can also inherit from a built-in element:

theme::Theme::registerElement(cSearchBox, theme::Element::Surface);

After that, unresolved search-box selectors fall back through Surface before they reach the base sheet.

Rule Matching

Theme resolution follows a fallback chain instead of returning just the single most specific rule. Starting at the requested selector, the theme looks for an authored sheet and then resolves its base sheet:

  • tags fall back to the best authored tag subset, then to no tags

  • states fall back to the best authored state subset, then to no states

  • parts fall back through the element base hierarchy for the same part, then to the same element without a part

  • elements fall back through their registered base element, then to Element::Base

When several tag or state subsets could match, the larger subset wins. If two candidates are equally specific, the later edited selector wins. The resolved PropertySheet inherits every unspecified property from its base sheet.

Tags

Tags are compact bit identifiers registered on the builder that creates a theme. A tag has no built-in string name; keep the returned Tag value in your application code and use it consistently for selectors and surfaces:

auto builder = theme::ThemeBuilder::from(theme::Theme::dark());
const auto warning = builder.registerTag();

builder.edit(theme::Selector{theme::Element::StatusLine, theme::Part::Background}.requireTag(warning))
    .setColor(Color{fg::BrightYellow, bg::Red});

app.setTheme(builder.build());
statusLine->themeAttributes().setTags(theme::Tags{warning});
statusLine->flags().setThemeOutdated();

Use tags for visual variants that are not general UI states. For example, warning, primary, or inspector are good tag candidates. Focus, selection, checked, and disabled state belong to SurfaceFlags and are translated into theme states automatically.

Building Themes

You normally start with one of the predefined builders and add only the rules that make your application different. The following example keeps the default dark structure, changes the regular status line, and gives a tagged status line a stronger background:

using namespace erbsland::cterm;

auto builder = theme::ThemeBuilder::dark();
const auto warning = builder.registerTag();

builder.edit(theme::Selector{theme::Element::StatusLine, theme::Part::Background})
    .setColor(Color{fg::BrightWhite, bg::Blue});
builder.edit(theme::Selector{theme::Element::StatusLine, theme::Part::Background}.requireTag(warning))
    .setStyle(CharStyle{Color{fg::BrightYellow, bg::Red}, CharAttributes::Bold});

app.setTheme(builder.build());

The same editor handles text styling and structural blocks. Action-help formatting, for example, uses colors for key text, bracket text, and action names. The ActionHelp/KeyBracket sheet also supplies the bracket glyphs: BlockRole::West is the opening bracket, BlockRole::Center separates multiple keys, and BlockRole::East is the closing bracket.

auto builder = theme::ThemeBuilder::dark();
builder.edit(theme::Selector{theme::Element::ActionHelp, theme::Part::KeyBracket})
    .setColor(Color{fg::BrightBlack, bg::Inherited})
    .setBlocks(U"   </>          ");

app.setTheme(builder.build());

Block tables contain sixteen code points named by BlockRole. Use setBlock() for one role, or setBlocks() with a 9-character or 16-character UTF-32 string.

Margins and padding have different owners. Margins are outside a themed part; the parent surface keeps that space and the part does not paint it. Padding is inside the themed part; text-focused UI elements paint padding with the part’s BlockRole::Background block and style before drawing the text. Strict one-line text ignores top and bottom margins and padding but still uses left and right values. When adjacent one-line text parts meet, their horizontal margins collapse to the larger value instead of being added together.

Scroll bars use the track margins to decide where the thumb may appear:

auto builder = theme::ThemeBuilder::dark();
builder.edit(theme::Selector{theme::Element::VerticalScrollBar, theme::Part::Track})
    .setBlocks(U"░░░░░░░░░░░░↑░↓↑")
    .setMargins(Margins{0, 1});
builder.edit(theme::Selector{theme::Element::VerticalScrollBar, theme::Part::Thumb})
    .setBlocks(U"            ▔ ▁ ");

app.setTheme(builder.build());

Using Themes In Custom Surfaces

Each surface has theme attributes for its element and tags. Built-in surfaces set their element automatically. Custom surfaces should set the element in their constructor:

class SearchBox final : public ui::Surface {
public:
    explicit SearchBox(ProtectedTag) : Surface{cSearchBox} {}

    [[nodiscard]] static auto create() -> std::shared_ptr<SearchBox> {
        return std::make_shared<SearchBox>(ProtectedTag{});
    }

protected:
    void onPaint(WritableBuffer &buffer, const ui::PaintContext &context) noexcept override {
        auto backgroundTheme = context.theme().forPart(theme::Part::Background);
        theme::ThemePainter{buffer, backgroundTheme}.fill(context.surfaceRect());

        const auto textRect = backgroundTheme.contentRect(context.surfaceRect());
        const auto textColor = context.theme().forPart(theme::Part::Text).color();
        buffer.drawText(_text, textRect, Alignment::CenterLeft, textColor);
    }

private:
    String _text;
};

For layout decisions that depend on themed margins, override onLayout(Size, const LayoutContext &) and use LayoutContext there. The same theme scope is then used during layout and paint:

void SearchBox::onLayout(Size newParentSize, const ui::LayoutContext &context) noexcept {
    const auto contentRect = context.theme().forPart(theme::Part::Background).contentRect(
        Rectangle{Position{}, newParentSize});
    _contentWidth = contentRect.width();
}

Runtime Updates

When you call Application::setTheme(), Page::setTheme(), or SurfaceFlags::setThemeOutdated(), the affected surface tree marks layout and paint as outdated. This is intentional: a color-only change may only alter cells, while a block or margin change may also alter content rectangles and therefore layout size.

Changing surface->themeAttributes().setElement(...) or surface->themeAttributes().setTags(...) is passive. This keeps constructor-time setup cheap and predictable. If you change an element or tag dynamically, call surface->flags().setThemeOutdated() yourself. State-like changes such as setEnabled(false), setSelected(true), and setChecked(true) belong to SurfaceFlags and invalidate the theme scope automatically.

Paint code should use the resolved context helpers instead of querying a global object:

const auto backgroundTheme = context.theme().forPart(theme::Part::Background);
buffer.fill(context.surfaceRect(), backgroundTheme.block());

The ThemePainter is a thin convenience wrapper around WritableBuffer and ThemeAccessor. It is not a separate style engine; it simply fills or frames rectangles with blocks resolved through the active theme scope.

Predefined Themes

The library provides predefined builders:

It also provides ready-to-use immutable themes:

Reference

class Element

A fast theme element identifier.

Public Types

using Id = Identifier<IdentifierType::Element>

The underlying element identifier.

Public Functions

constexpr Element() noexcept = default

Create an empty element identifier.

inline constexpr Element(const Id id) noexcept

Create an element from an element identifier.

Parameters:

id – The element identifier.

inline constexpr bool operator==(const Element &other) const noexcept

Compare two element identifiers.

inline constexpr bool operator!=(const Element &other) const noexcept

Compare two element identifiers.

inline constexpr uint16_t value() const noexcept

Get the numeric identifier.

inline constexpr bool isValid() const noexcept

Test if this identifier is non-empty.

inline constexpr std::size_t hash() const noexcept

Get a stable hash for this element identifier.

Public Static Functions

static inline constexpr Element custom(const int32_t id)

Create a custom element identifier.

Parameters:

id – A stable application-defined positive identifier.

Throws:
  • std::invalid_argument – If id is not positive.

  • std::out_of_range – If id exceeds the supported custom identifier range.

Public Static Attributes

static const Element None = {Id{0}}

No specific element.

static const Element Base = {Id{1}}

Root element that provides defaults for every theme.

static const Element Page = {Id{2}}

Generic page root.

static const Element Layout = {Id{3}}

Generic layout element.

static const Element Surface = {Id{4}}

Generic surface/control element.

static const Element Panel = {Id{5}}

A generic panel element.

static const Element TextBox = {Id{6}}

A text box element.

static const Element StatusLine = {Id{7}}

A status line element.

static const Element HeaderLine = {Id{8}}

A header line element.

static const Element FooterLine = {Id{9}}

A footer line element.

static const Element ActionHelp = {Id{10}}

An action help element.

static const Element HorizontalScrollBar = {Id{11}}

A horizontal scroll bar element.

static const Element VerticalScrollBar = {Id{12}}

A vertical scroll bar element.

static const Element ScrollCorner = {Id{13}}

A scroll corner element.

static const Element Sections = {Id{14}}

A vertical sections layout element.

static const Element Buttons = {Id{15}}

A horizontal button layout element.

static const Element Button = {Id{16}}

A focusable action button element.

static const Element Frame = {Id{17}}

A generic framed one-child layout element.

static const Element Choice = {Id{18}}

A modal choice page element.

static const Element HelpViewer = {Id{19}}

The help viewer page.

static const Element StaticText = {Id{20}}

A one-line static text surface.

static const Element Label = {Id{21}}

A label text surface.

static const Element DynamicText = {Id{22}}

A dynamic text surface.

enum class erbsland::cterm::theme::IdentifierType : uint8_t

The logical kind of a theme identifier.

Values:

enumerator Element

Theme element identifiers.

enumerator Part

Theme part identifiers.

template<IdentifierType tIdentifierType>
class Identifier

A strongly typed numeric theme identifier.

Template Parameters:

tIdentifierType – The logical identifier kind.

Public Functions

constexpr Identifier() noexcept = default

Create an invalid identifier.

inline explicit constexpr Identifier(const uint16_t value) noexcept

Create an identifier from a raw value.

Parameters:

value – The raw identifier value.

bool operator==(const Identifier &other) const noexcept = default

Compare two identifiers.

bool operator!=(const Identifier &other) const noexcept = default

Compare two identifiers.

inline constexpr uint16_t value() const noexcept

Get the raw identifier value.

Returns:

The raw identifier value.

inline constexpr bool isValid() const noexcept

Test if this identifier is non-empty.

Returns:

true if this identifier has a positive value.

inline constexpr std::size_t hash() const noexcept

Get a stable hash for this identifier.

Returns:

The hash value.

class Part

A fast theme part identifier.

Parts identify the visual role inside an element, for example a scrollbar track or thumb.

Public Types

using Id = Identifier<IdentifierType::Part>

The underlying part identifier.

Public Functions

constexpr Part() noexcept = default

Create an empty part identifier.

inline constexpr Part(const Id id) noexcept

Create a part from a part identifier.

Parameters:

id – The non-zero part identifier.

bool operator==(const Part &other) const noexcept = default

Compare two part identifiers.

bool operator!=(const Part &other) const noexcept = default

Compare two part identifiers.

inline constexpr uint16_t value() const noexcept

Get the numeric identifier.

Returns:

The numeric identifier.

inline constexpr bool isValid() const noexcept

Test if this identifier is non-empty.

Returns:

true if this part has a non-zero identifier.

inline constexpr std::size_t hash() const noexcept

Get a stable hash for this part identifier.

Returns:

The hash value.

Public Static Functions

static inline constexpr Part custom(const int32_t id)

Create a custom part identifier.

Parameters:

id – A stable application-defined positive identifier.

Throws:
  • std::invalid_argument – If id is not positive.

  • std::out_of_range – If id exceeds the supported custom identifier range.

Returns:

The custom part identifier.

Public Static Attributes

static const Part None = {Id{0}}

No specific part.

static const Part Background = {Id{1}}

Background fill.

static const Part Text = {Id{2}}

Text content.

static const Part Border = {Id{3}}

Border or frame.

static const Part Track = {Id{4}}

Scrollbar or slider track.

static const Part Thumb = {Id{5}}

Scrollbar or slider thumb.

static const Part Decrease = {Id{6}}

Decrease button or indicator.

static const Part Increase = {Id{7}}

Increase button or indicator.

static const Part Indicator = {Id{8}}

Generic indicator part.

static const Part Key = {Id{9}}

Key label in the shortcut help.

static const Part KeyBracket = {Id{10}}

Bracket text around a key label in shortcut help.

static const Part ActionName = {Id{11}}

The action name after a keyboard shortcut.

static const Part Title = {Id{12}}

Title text in a decorated element.

static const Part TitleBracket = {Id{13}}

Brackets around the title text.

static const Part Spacing = {Id{14}}

Spacing configuration between repeated elements.

static const Part Ellipsis = {Id{15}}

Ellipsis marker for cropped text.

class State

A single UI state flag used in theme selectors.

Public Functions

constexpr State() noexcept = default

Create an empty state flag.

inline explicit constexpr State(const uint8_t mask) noexcept

Create a state from a bit mask.

Parameters:

mask – The state bit mask.

bool operator==(const State &other) const noexcept = default

Compare two state flags.

bool operator!=(const State &other) const noexcept = default

Compare two state flags.

inline constexpr uint8_t mask() const noexcept

Get the bit mask for this state.

Returns:

The bit mask.

inline constexpr bool isValid() const noexcept

Test if this state has a non-zero bit mask.

Returns:

true if this state is usable in a selector.

Public Static Attributes

static const State Focused = {1U << 0U}

The surface has keyboard focus.

static const State FocusWithin = {1U << 1U}

The surface contains keyboard focus in its subtree.

static const State Selected = {1U << 2U}

The surface or item is selected.

static const State Disabled = {1U << 3U}

The surface or item is disabled.

static const State Checked = {1U << 4U}

The surface or item is checked.

class States

A compact set of UI state flags used in theme selector matching.

Public Functions

constexpr States() noexcept = default

Create an empty state set.

inline constexpr States(const State state) noexcept

Create a state set containing one state.

Parameters:

state – The initial state.

inline explicit constexpr States(const uint8_t bits) noexcept

Create a state set from raw bits.

Parameters:

bits – The raw state bits.

States(std::initializer_list<State> states) noexcept

Create a state set from an initializer list.

Parameters:

states – The states to add.

bool operator==(const States &other) const noexcept = default

Compare two state sets.

bool operator!=(const States &other) const noexcept = default

Compare two state sets.

inline States operator|(const States &other) const noexcept

Combine two state sets.

inline States &operator|=(const States &other) noexcept

Add all states from another state set.

inline void add(const State state) noexcept

Add one state.

Parameters:

state – The state to add.

inline void remove(const State state) noexcept

Remove one state.

Parameters:

state – The state to remove.

inline constexpr bool contains(const State state) const noexcept

Test if one state is present.

Parameters:

state – The state to test.

inline constexpr bool containsAll(const States states) const noexcept

Test if all states from another set are present.

Parameters:

states – The required states.

inline constexpr bool empty() const noexcept

Test if the set is empty.

inline int size() const noexcept

Get the number of states in the set.

inline constexpr uint8_t bits() const noexcept

Get the raw state bits.

inline constexpr std::size_t hash() const noexcept

Get a stable hash for this state set.

class Tag

A theme tag bit assigned by a theme builder.

Public Functions

constexpr Tag() noexcept = default

Create an empty tag.

inline explicit constexpr Tag(const uint8_t bit) noexcept

Create a tag from a zero-based bit index.

Parameters:

bit – The zero-based tag bit index.

bool operator==(const Tag &other) const noexcept = default

Compare two tags.

bool operator!=(const Tag &other) const noexcept = default

Compare two tags.

inline constexpr uint8_t value() const noexcept

Get the one-based numeric tag value, or zero for an empty tag.

inline constexpr uint8_t bit() const noexcept

Get the zero-based tag bit index.

inline constexpr uint64_t mask() const noexcept

Get the mask for this tag.

inline constexpr bool isValid() const noexcept

Test if this tag is non-empty.

Returns:

true if this tag references a valid bit.

inline constexpr std::size_t hash() const noexcept

Get a stable hash for this tag.

Returns:

The hash value.

class Tags

A compact set of theme tags.

Public Functions

constexpr Tags() noexcept = default

Create an empty tag set.

inline constexpr Tags(const Tag tag) noexcept

Create a tag set containing one tag.

Parameters:

tag – The initial tag.

inline explicit constexpr Tags(const uint64_t bits) noexcept

Create a tag set from raw bits.

Parameters:

bits – The raw tag bits.

inline constexpr Tags(std::initializer_list<Tag> tags) noexcept

Create a tag set from an initializer list.

Parameters:

tags – The tags to add.

bool operator==(const Tags &other) const noexcept = default

Compare two tag sets.

bool operator!=(const Tags &other) const noexcept = default

Compare two tag sets.

inline constexpr Tags operator|(const Tags &other) const noexcept

Combine two tag sets.

inline constexpr Tags &operator|=(const Tags &other) noexcept

Add all tags from another tag set.

inline constexpr void add(const Tag tag) noexcept

Add one tag.

Parameters:

tag – The tag to add.

inline constexpr void add(const Tags tags) noexcept

Add all tags from another tag set.

Parameters:

tags – The tags to add.

inline constexpr void remove(const Tag tag) noexcept

Remove one tag.

Parameters:

tag – The tag to remove.

inline constexpr bool contains(const Tag tag) const noexcept

Test if one tag is present.

Parameters:

tag – The tag to test.

inline constexpr bool containsAll(const Tags tags) const noexcept

Test if all tags from another set are present.

Parameters:

tags – The required tags.

inline constexpr bool empty() const noexcept

Test if the set is empty.

inline int size() const noexcept

Get the number of tags in the set.

inline constexpr uint64_t bits() const noexcept

Get the raw tag bits.

inline constexpr std::size_t hash() const noexcept

Get a stable hash for this tag set.

class Selector

A selector for one theme property sheet.

Public Functions

constexpr Selector() noexcept = default

Create an empty selector.

inline explicit constexpr Selector(const Element element, const Part part = Part::None) noexcept

Create a selector for an element and optional part.

Parameters:
  • element – The selected element.

  • part – The selected part.

bool operator==(const Selector &other) const noexcept = default

Compare two selectors.

bool operator!=(const Selector &other) const noexcept = default

Compare two selectors.

inline constexpr Element element() const noexcept

Get the selected element.

inline constexpr Part part() const noexcept

Get the selected part.

inline constexpr States states() const noexcept

Get the required states.

inline constexpr Tags tags() const noexcept

Get the required tags.

inline constexpr Selector withElement(const Element element) const noexcept

Create a copy with a different element.

inline constexpr Selector withPart(const Part part) const noexcept

Create a copy with a different part.

inline Selector withState(const State state) const noexcept

Create a copy with additional state flags.

inline constexpr Selector withStates(const States states) const noexcept

Create a copy with a state set.

inline constexpr Selector withTag(const Tag tag) const noexcept

Create a copy with an additional tag.

inline constexpr Selector withTags(const Tags tags) const noexcept

Create a copy with a tag set.

Selector &requireState(State state) noexcept

Add one required state to this selector.

Selector &requireStates(States states) noexcept

Add required states to this selector.

Selector &requireTag(Tag tag) noexcept

Add one required tag to this selector.

Selector &requireTags(Tags tags) noexcept

Add required tags to this selector.

bool matches(States state, Tags tags) const noexcept

Test if this selector matches the supplied state and tags.

int specificity() const noexcept

Get the selector specificity.

std::size_t hash() const noexcept

Get a stable hash for this selector.

enum class erbsland::cterm::theme::BlockRole : uint8_t

Named roles for the sixteen themed block code points.

Values:

enumerator NorthWest

Top-left block.

enumerator North

Top edge block.

enumerator NorthEast

Top-right block.

enumerator West

Left edge block.

enumerator Center

Center block.

enumerator East

Right edge block.

enumerator SouthWest

Bottom-left block.

enumerator South

Bottom edge block.

enumerator SouthEast

Bottom-right block.

enumerator HorizontalWest

Left block for one-row rectangles.

enumerator HorizontalCenter

Center block for one-row rectangles.

enumerator HorizontalEast

Right block for one-row rectangles.

enumerator VerticalNorth

Top block for one-column rectangles.

enumerator VerticalCenter

Center block for one-column rectangles.

enumerator VerticalSouth

Bottom block for one-column rectangles.

enumerator Single

Single-cell block.

enumerator Main

Alias for the main block, if only one is relevant.

enumerator LeftPadding

Padding on the left side.

enumerator RightPadding

Padding on the right side.

enumerator LeftBracket

Left brackets.

enumerator MiddleBracket

Middle “brackets” (separator).

enumerator RightBracket

Right brackets.

enumerator LeftOuterPadding

Left outer padding (after left bracket).

enumerator RightOuterPadding

Right outer padding (before right bracket).

enumerator LeftInnerPadding

Left inner padding (after middle bracket).

enumerator RightInnerPadding

Right inner padding (before middle bracket).

class Properties

Authoring values for one theme property sheet.

Public Types

using Blocks = std::array<char32_t, cBlockCount>

The block code point table.

Public Functions

inline const std::optional<Color> &color() const noexcept

Access the optional color replacement.

inline void setColor(Color color) noexcept

Replace the optional color replacement.

inline const std::optional<ColorSequence> &colorSequence() const noexcept

Access the optional color sequence.

inline void setColorSequence(ColorSequence colorSequence) noexcept

Replace the optional color sequence.

inline const std::optional<CharAttributes> &attributes() const noexcept

Access the optional attributes replacement.

inline void setAttributes(CharAttributes attributes) noexcept

Replace the optional attributes replacement.

inline const std::optional<Blocks> &blocks() const noexcept

Access the optional block table.

void setBlock(BlockRole role, char32_t codePoint) noexcept

Replace one block role.

inline void setBlocks(Blocks blocks) noexcept

Replace the complete block table.

inline const std::optional<Margins> &margins() const noexcept

Access the optional margins.

Margins are always zero or positive.

inline void setMargins(const Margins margins) noexcept

Replace the optional margins.

Margins are outside a themed part and stay owned by the parent surface.

inline const std::optional<Margins> &padding() const noexcept

Access the optional padding.

Padding is always zero or positive.

inline void setPadding(const Margins padding) noexcept

Replace the optional padding.

Padding is inside a themed part and is painted by text-focused UI elements.

Public Static Attributes

static constexpr auto cBlockCount = std::size_t{16}

Number of block roles stored in a property sheet.

static constexpr auto cInheritedBlock = char32_t{0x200BU}

The special code-point for inherited blocks that inherit from the parent.

class PropertyEditor

Mutable editor for one property sheet while a theme is being built.

Public Functions

PropertyEditor() = default

Create an empty editor.

inline explicit PropertyEditor(Properties &properties) noexcept

Create an editor for a property sheet.

Parameters:

properties – The edited properties. Must outlive this editor.

PropertyEditor &setColor(Color color) noexcept

Set the color replacement.

PropertyEditor &setColor(Foreground::Hue fg) noexcept

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

PropertyEditor &setColor(Background::Hue bg) noexcept

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

PropertyEditor &setColor(Foreground::Hue fg, Background::Hue bg) noexcept

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

PropertyEditor &setColorSequence(ColorSequence colorSequence) noexcept

Set the color sequence.

PropertyEditor &setAttributes(CharAttributes attributes) noexcept

Set the “attributes” replacement.

PropertyEditor &setStyle(CharStyle style) noexcept

Set color and attributes in one call.

PropertyEditor &setBlock(BlockRole role, char32_t codePoint) noexcept

Set one block code point.

PropertyEditor &setInheritedBlock(BlockRole role) noexcept

Set one block to inherit from its parent.

PropertyEditor &setBlocks(std::u32string_view blocks)

Set a 9-block or 16-block table from UTF-32 code points.

PropertyEditor &setBlocks(char32_t codePoint)

Set all blocks to a given code point.

PropertyEditor &setInheritedBlocks() noexcept

Set all blocks to inherit from its parent.

PropertyEditor &setBracketBlocks(char32_t left, char32_t right, char32_t middle = U' ')

Set all bracket blocks to a given code point.

PropertyEditor &setMargins(Margins margins) noexcept

Set the margins.

Margins describe parent-owned space around a themed part. The part does not paint this area. Margins can never be negative. Negative margins are set to zero.

PropertyEditor &setPadding(Margins padding) noexcept

Set the padding.

Padding describes part-owned space inside the part. Text-focused UI elements paint this area with the part’s background block and style before drawing the text content. Padding can never be negative. Negative padding is set to zero.

class PropertySheet

Effective values for one resolved theme selector.

Public Functions

PropertySheet() noexcept

Create the root sheet with all default values.

PropertySheet(const PropertySheet &base, const Properties &properties) noexcept

Create an effective sheet from a base sheet and authoring properties.

Color color(std::size_t animationCycle = 0) const noexcept

Resolve the color for an animation cycle.

inline const ColorSequence &colorSequence() const noexcept

Access the effective color sequence.

inline CharAttributes attributes() const noexcept

Access the effective attributes.

CharStyle style(std::size_t animationCycle = 0) const noexcept

Resolve the style for an animation cycle.

Char block(BlockRole role, std::size_t animationCycle = 0) const noexcept

Resolve a block character for a role.

Tile9Style tile9Style(std::size_t animationCycle = 0) const noexcept

Resolve a tile-9 style for an animation cycle.

inline Margins margins() const noexcept

Access the effective parent-owned margins outside the part.

inline Margins padding() const noexcept

Access the effective part-owned padding inside the part.

using erbsland::cterm::theme::PropertySheetPtr = std::shared_ptr<PropertySheet>
using erbsland::cterm::theme::PropertySheetConstPtr = std::shared_ptr<const PropertySheet>
class ThemeBuilder

Builder for immutable themes.

Public Functions

ThemeBuilder() = default

Create an empty theme builder.

Tag registerTag()

Register a new theme tag.

Throws:

std::logic_error – After 64 tags.

PropertyEditor edit(Selector selector)

Edit one property sheet.

Parameters:

selector – The selector for the sheet.

ThemeConstPtr build() const

Build an immutable theme.

Public Static Functions

static ThemeBuilder from(const ThemeConstPtr &theme)

Create a builder from an existing theme.

static ThemeBuilder dark()

Create the dark default theme builder.

static ThemeBuilder light()

Create the light default theme builder.

static ThemeBuilder monochrome()

Create the monochrome default theme builder.

static ThemeBuilder zero()

Create the zero theme builder.

class Theme

Immutable theme built from hierarchical property sheets.

Public Types

using OptionalDefinition = std::optional<std::reference_wrapper<const Definition>>

An optional definition.

using Definitions = std::unordered_map<Selector, Definition>

The authored property sheet definitions.

Public Functions

explicit Theme(Definitions definitions, uint8_t registeredTagCount = 0)

Create a theme from authored property sheet definitions.

Parameters:
  • definitions – The authored property sheet definitions.

  • registeredTagCount – The number of registered tags.

PropertySheetConstPtr propertySheet(Selector selector) const

Resolve an effective property sheet for a selector.

Parameters:

selector – The selector to resolve.

inline uint8_t registeredTagCount() const noexcept

Get the number of registered tags.

inline const Definitions &definitions() const noexcept

Access the authored definitions.

Public Static Functions

static void registerElement(Element element, Element base)

Register a user-defined element base relationship.

Parameters:
  • element – The user-defined element.

  • base – The direct base element.

static const ThemeConstPtr &dark() noexcept

Access the predefined dark theme.

static const ThemeConstPtr &light() noexcept

Access the predefined light theme.

static const ThemeConstPtr &monochrome() noexcept

Access the predefined monochrome theme.

static const ThemeConstPtr &zero() noexcept

Access the predefined zero theme.

The zero theme does not define any elements or styles. Use this theme for your unit tests or when you want to start from a clean slate.

struct Definition

One authored property sheet definition.

Public Members

Properties properties

The authored values.

uint64_t order = {}

The insertion order.

using erbsland::cterm::theme::ThemePtr = std::shared_ptr<Theme>
using erbsland::cterm::theme::ThemeConstPtr = std::shared_ptr<const Theme>
class ThemeAccessor

Read-only theme accessor for the current selector.

Public Functions

inline ThemeAccessor() noexcept

Create an accessor for the default theme root.

inline ThemeAccessor(ThemeConstPtr theme, const Selector selector) noexcept

Create an accessor.

Parameters:
  • theme – The active theme. Must not be null.

  • selector – The selector.

inline const Theme &theme() const noexcept

Access the active theme.

inline Selector selector() const noexcept

Access the selected selector.

ThemeAccessor forPart(Part part) const noexcept

Create an accessor for another part.

ThemeAccessor withStates(States states) const noexcept

Create an accessor with additional states.

ThemeAccessor withTags(Tags tags) const noexcept

Create an accessor with additional tags.

inline Color color(std::size_t animationCycle = 0) const noexcept

Resolve the color for an animation cycle.

inline const ColorSequence &colorSequence() const noexcept

Access the effective color sequence.

inline CharAttributes attributes() const noexcept

Access the effective attributes.

inline CharStyle style(const std::size_t animationCycle = 0) const noexcept

Resolve the style for an animation cycle.

inline Char block(const BlockRole role = BlockRole::Main, const std::size_t animationCycle = 0) const noexcept

Resolve a block character for a role.

inline Tile9Style tile9Style(const std::size_t animationCycle = 0) const noexcept

Resolve the tile-9 style for an animation cycle.

inline Margins margins() const noexcept

Access the effective parent-owned margins outside the current part.

Margins are always zero or positive.

inline Margins horizontalMargins() const noexcept

Access the effective horizontal margins with top and bottom set to zero.

Use this for strict one-line text where vertical margins are intentionally ignored. Margins are always zero or positive.

inline Margins padding() const noexcept

Access the effective part-owned padding inside the current part.

Padding is always zero or positive.

inline Margins horizontalPadding() const noexcept

Access the effective horizontal padding with top and bottom set to zero.

Use this for strict one-line text where vertical padding is intentionally ignored. Padding is always zero or positive.

inline Size extent() const noexcept

Calculate the size consumed by margins and padding.

Extent is always zero or positive.

inline Size horizontalExtent() const noexcept

Calculate the horizontal size consumed by margins and padding.

Use this for strict one-line text where vertical margins and padding are intentionally ignored. Extent is always zero or positive.

inline LayoutRectangles layout(const Rectangle outerRect) const noexcept

Resolve the themed layout rectangles for an assigned rectangle.

Parameters:

outerRect – The assigned rectangle, including margins.

Returns:

The resolved rectangles.

inline LayoutRectangles horizontalLayout(const Rectangle outerRect) const noexcept

Resolve horizontal-only themed layout rectangles for an assigned rectangle.

Use this for strict one-line text where vertical margins and padding are intentionally ignored.

Parameters:

outerRect – The assigned rectangle, including horizontal margins.

Returns:

The resolved rectangles.

inline Rectangle contentRect(const Rectangle rect) const noexcept

Resolve the content rectangle by applying margins.

This only removes the parent-owned margin area. Apply padding() separately when resolving the inner content of a part.

class ThemePainter

Thin convenience painter for rendering themed blocks into a writable buffer.

Public Functions

ThemePainter(WritableBuffer &buffer, ThemeAccessor theme) noexcept

Create a themed painter.

Parameters:
  • buffer – The target buffer.

  • theme – The theme accessor to render.

void fill(Rectangle rect) const noexcept

Fill a rectangle using a tile-9 pattern from the accessor.

Parameters:

rect – The rectangle to fill.

void fillBackground(Rectangle rect) const noexcept

Fill the background using Part::Background, and a tile-9 pattern from accessor.

void drawFrame(Rectangle rect) const noexcept

Draw only the edge blocks for a rectangle.

Parameters:

rect – The rectangle to draw.