Color, style and CSS

ManyUI.ANSI16_PALETTEConstant

The 16 system colors, in the xterm/VGA arrangement: 0:7 are the dim half, 8:15 the bright half. [I] data owned by this file.

source
ManyUI.ANSI256_PALETTEConstant

The xterm 256-color palette: 0:15 system, 16:231 the 6x6x6 cube (16 + 36r + 6g + b), 232:255 the grey ramp (8 + 10i). [I].

source
ManyUI.ColorType
struct Color

A 4-byte tagged color value. isbits, deliberately NOT an abstract hierarchy: Style is a field of Cell and a Matrix{Cell} is copied every frame.

Fields

  • kind::ManyUI.ColorKind.T: How to read r, g, b.

  • r::UInt8: Red channel, or the palette index for ANSI16/ANSI256.

  • g::UInt8: Green channel.

  • b::UInt8: Blue channel.

source
Base.parseMethod
parse(_::Type{Color}, s::AbstractString) -> Color

Parse a color, throwing ArgumentError on garbage. Pure.

source
Base.tryparseMethod
tryparse(
    _::Type{Color},
    s::AbstractString
) -> Union{Nothing, Color}

Parse a color. Returns nothing instead of throwing.

Accepted forms: #rgb, #rrggbb, rgb(255,136,0), red, bright_black, ansi(9), color(200), default, transparent (-> COLOR_UNSET). Pure.

source
ManyUI._linearizeMethod
_linearize(v::UInt8) -> Float64

Decode one sRGB byte to a linear 0..1 intensity. [I], and the reason the metric below is perceptual rather than a raw byte difference.

source
ManyUI.ansi16Method
ansi16(i::Integer) -> Color

A 16-color palette entry, 0 <= i <= 15. Throws ArgumentError otherwise. Pure.

source
ManyUI.ansi256Method
ansi256(i::Integer) -> Color

A 256-color palette entry, 0 <= i <= 255. Throws ArgumentError otherwise. Pure.

source
ManyUI.colorMethod
color(name::Symbol) -> Color

Look up a named color, e.g. color(:bright_black). Throws KeyError when unknown. Pure.

source
ManyUI.color_distanceMethod
color_distance(a::Color, b::Color) -> Float64

Weighted-RGB distance in LINEAR (sRGB-decoded) space – raw-byte distance maps mid-greys to blue.

NORMATIVE tie-break: when two candidates are equidistant the LOWER palette index wins. Without this, rgb_to_ansi256's test vectors are implementation-dependent. Pure.

source
ManyUI.degradeMethod
degrade(c::Color, depth::ManyUI.ColorDepth.T) -> Color

X1. Map an authorial-intent color onto what depth can show.

Pure, TOTAL and IDEMPOTENT: degrade(degrade(c, d), d) === degrade(c, d).

Called ONLY from AnsiEncoder. Never in the buffer, never in layout. At MONOCHROME the result is white or black by luminance – never COLOR_DEFAULT, which would discard the fg/bg distinction.

kind \ depthTRUECOLORANSI256ANSI16MONOCHROME
RGBidentityrgb_to_ansi256rgb_to_ansi16lum >= 0.5 ? 15 : 0
ANSI256identityidentityansi256_to_ansi16as above
ANSI16identityidentityidentityidx in (7, 15) ? 15 : 0
DEFAULTidentityidentityidentityidentity
UNSETidentityidentityidentityidentity
source
ManyUI.detect_color_depthFunction
detect_color_depth() -> ManyUI.ColorDepth.T
detect_color_depth(env::AbstractDict) -> ManyUI.ColorDepth.T

Pure, table-testable environment probe – no globals, no side effects.

NORMATIVE rules, in order:

NO_COLOR set (any value)         -> MONOCHROME
COLORTERM in {truecolor, 24bit}  -> TRUECOLOR
TERM contains "256color"         -> ANSI256
TERM == "dumb" or TERM unset     -> MONOCHROME
otherwise                        -> ANSI16
source
ManyUI.is_setMethod
is_set(c::Color) -> Bool

True when c specifies something. Pure.

A TOKEN counts as set: it NAMES a colour, it just has not been looked up yet. Deciding otherwise would make merge drop it and a themed rule would silently lose to the one under it.

source
ManyUI.is_tokenMethod
is_token(c::Color) -> Bool

True when c is a semantic theme token rather than a colour. See theme.jl – this file owns the KIND, theme.jl owns what the payload means and when it is looked up. Pure.

source
ManyUI.is_unsetMethod
is_unset(c::Color) -> Bool

True when c specifies nothing and must inherit. Pure.

source
ManyUI.luminanceMethod
luminance(c::Color) -> Float64

Relative luminance in 0..1, computed from sRGB-decoded (linear) channels. Pure.

source
ManyUI.rgbMethod
rgb(r::Integer, g::Integer, b::Integer) -> Color

A TrueColor value from three 0:255 channels. Pure.

source
ManyUI.rgbMethod
rgb(hex::Integer) -> Color

A TrueColor value from a packed hex literal, e.g. rgb(0xff8800). Accepts 0 <= hex <= 0xffffff. Pure.

source
ManyUI.rgb_to_ansi16Method
rgb_to_ansi16(c::Color) -> Color

NORMATIVE: defined AS the composition, not independently.

rgb_to_ansi16(c) === ansi256_to_ansi16(rgb_to_ansi256(c))

One tested table; TrueColor->16 and TrueColor->256->16 cannot disagree. Pure.

source
ManyUI.rgb_to_ansi256Method
rgb_to_ansi256(c::Color) -> Color

Nearest 256-color entry: the 6x6x6 cube, the 24 greys, and the 16 system colors. Pure.

source
ManyUI.to_rgbMethod
to_rgb(c::Color) -> Color

Resolve ANSI16/ANSI256 to RGB via the palette; identity on RGB.

Throws ArgumentError on UNSET/DEFAULT – they have no RGB value. Pure.

source
ManyUI.StyleType
struct Style

A resolved text style: two colors plus a tri-state attribute set. isbits, 12 bytes.

attrs holds attribute VALUES; mask holds which of them are EXPLICITLY SPECIFIED. The tri-state is required: without the mask, bold: false in a stylesheet cannot override an inherited bold – the same problem COLOR_UNSET solves for colors.

Fields

  • fg::Color: Foreground color; COLOR_UNSET means inherit.

  • bg::Color: Background color; COLOR_UNSET means inherit. Not inheritable.

  • attrs::UInt16: Attribute values: bit set means the attribute is ON.

  • mask::UInt16: Attribute mask: bit set means the attribute is SPECIFIED.

source
ManyUI.StyleMethod
Style(
;
    fg,
    bg,
    bold,
    dim,
    italic,
    underline,
    blink,
    reverse,
    hidden,
    strike
) -> Style

Keyword constructor. nothing leaves an attribute UNSPECIFIED; true and false both SET the mask, with the value on or off respectively.

source
Base.mergeMethod
merge(base::Style, over::Style) -> Style

Right-biased, per-property merge: over wins wherever it specifies.

A monoid – associative, with identity STYLE_NONE. This IS the cascade fold. NORMATIVE:

fg    = is_set(over.fg) ? over.fg : base.fg
bg    = is_set(over.bg) ? over.bg : base.bg
mask  = base.mask | over.mask
attrs = (base.attrs & ~over.mask) | (over.attrs & over.mask)

Pure.

source
ManyUI.degradeMethod
degrade(s::Style, depth::ManyUI.ColorDepth.T) -> Style

X1. Degrade fg and bg to depth.

At MONOCHROME, also drops DIM/ITALIC/BLINK/STRIKE/HIDDEN from the mask and keeps BOLD/UNDERLINE/REVERSE. Pure and idempotent.

source
ManyUI.hasMethod
has(s::Style, a::ManyUI.Attr.T) -> Bool

True when a is both specified AND on. Pure.

source
ManyUI.inheritableMethod
inheritable(s::Style) -> Style

The inheritable subset of s: the foreground and all text attributes. NOT the background. Pure.

source
ManyUI.parse_attrsMethod
parse_attrs(s::AbstractString) -> Tuple{UInt16, UInt16}

Parse a text-style property value into (attrs, mask).

"bold italic" sets both bits in both words. A leading no- sets the mask with the value off: "no-bold" -> attrs = 0, mask = BOLD. Pure.

source
ManyUI.resolveMethod
resolve(s::Style) -> Style

Replace UNSET colors with DEFAULT. Call once, at the root, before emission. Pure.

source
ManyUI.specifiedMethod
specified(s::Style, a::ManyUI.Attr.T) -> Bool

True when a is specified at all, on or off. Pure.

source
ManyUI.withMethod
with(s::Style, a::ManyUI.Attr.T, on::Bool) -> Style

Copy of s with a specified and set to on. Pure.

source
ManyUI.withoutMethod
without(s::Style, a::ManyUI.Attr.T) -> Style

Copy of s with a unspecified – clears both the value and the mask. Pure.

source
ManyUI.ThemeType
struct Theme

A named palette: what each token means.

Partial by design – a token the theme does not name falls back to the one declared with register_token!, so a theme that cares about three colours is three entries long and still total.

Fields

  • name::Symbol: How the theme is asked for.

  • colors::Dict{Symbol, Color}: Token to colour. Need not be total.

source
ManyUI.register_token!Method
register_token!(name::Symbol, fallback::Color) -> UInt8

Declare a semantic colour called name, falling back to fallback in a theme that does not name it, and return its id.

Idempotent: re-declaring an existing name returns the id it already has and leaves its fallback alone. Ids are assigned in declaration order and are meaningful only within a session – they are an implementation detail of packing a token into an isbits Color, never something to serialise.

A fallback rather than an error at lookup time is deliberate: a partial theme is a usable theme, and the failure mode of the alternative is one unreadable widget discovered at runtime, far from the theme that caused it.

source
ManyUI.resolve_tokenFunction
resolve_token(c::Color) -> Color
resolve_token(c::Color, th::Theme) -> Color

c as a concrete colour under th: the token looked up, or c itself.

Idempotent, total, and identity-preserving for everything that is not a token – resolve_token(c) === c for an ordinary colour, so the common case allocates nothing and compares by identity.

source
ManyUI.resolve_tokenFunction
resolve_token(s::Style) -> Style
resolve_token(s::Style, th::Theme) -> Style

s with both colour planes resolved under th. Attributes are untouched: a theme names colours, not weights.

Returns s ITSELF when neither plane is a token, which is the overwhelming majority of styles on the emission path.

source
ManyUI.set_theme!Method
set_theme!(th::Theme) -> Theme

Put th in force and return it.

NOTHING IN THE TREE CHANGES, because nothing in the tree holds a resolved colour: tokens are looked up at emission. A caller therefore needs a full REPAINT and not a re-cascade – and not a re-parse of the stylesheet either. On the terminal backend that is refresh!; the frame diff will not find the change on its own, since the cells it compares are the same cells.

source
ManyUI.themeMethod
theme() -> Theme

The theme in force, or the registered theme called name.

source
ManyUI.theme_colorMethod
theme_color(th::Theme, name::Symbol) -> Color

What name means under th: the theme's own entry, or the token's declared fallback. Never a token – the result is always a colour.

source
ManyUI.tokenMethod
token(name::Symbol) -> Color

The Color naming the token name, to be looked up against a theme when it is painted.

Throws on an unknown name: a typo in a token is a typo in a colour, and silently painting the default instead is how a theme develops holes.

source
ManyUI.token_nameMethod
token_name(c::Color) -> Symbol

The name a token Color carries. Throws for anything else.

source
ManyUI.RICHTEXT_EMPTYConstant

The empty line. THE value a seam returns for "there is nothing here".

A shared constant rather than a fresh RichText(): border_title is asked of every node on every frame and the overwhelming majority have no caption, so the default answer must not allocate.

source
ManyUI.TextLikeType

What a widget will accept where it wants one line of text.

The seam every text-producing callback is typed against: a format, a cell or a caption may return either spelling, and the widget neither converts eagerly – which would allocate a RichText per row per frame for the overwhelmingly common plain case – nor grows a second code path. text_width, truncate_width and the painters all take this union, so the choice is the caller's and costs nothing when unused.

source
ManyUI.RichTextType
struct RichText

A single logical run of text whose style varies along it: a sequence of TextRuns, painted left to right.

NORMALISED at construction – empty runs dropped, adjacent runs with equal styles coalesced. The invariant costs one pass and buys a meaningful ==: RichText("ab") == RichText(TextRun("a"), TextRun("b")), so callers may build a line however is convenient without two spellings of the same line comparing unequal.

RichText is a VALUE, not a widget. It has no node, no identity and no dirty state; a widget holds one and repaints when it is replaced.

Fields

  • runs::Vector{TextRun}: The runs, in paint order. Normalised; never contains an empty run.
source
ManyUI.RichTextType
RichText(text::AbstractString) -> RichText
RichText(text::AbstractString, style::Style) -> RichText

A RichText of one run: text under style, which defaults to the painting widget's own style.

source
ManyUI.RichTextMethod
RichText(runs::TextRun...) -> RichText

A RichText of the given runs, in order.

source
ManyUI.TextRunType
struct TextRun

A run of text carrying its own style override.

style is folded OVER the painting widget's computed style with merge, the cascade's own monoid, so STYLE_NONE – the default – means "exactly the widget's style" and a run that names only bold inherits the widget's colours. A run therefore describes a DIFFERENCE, never an absolute appearance, which is what lets one RichText be painted into a light and a dark theme without being rebuilt.

Fields

  • text::String: The text of the run. May be empty; RichText drops such runs.

  • style::Style: The override, folded over the widget's style at paint time.

source
Base.:*Method
*(a::RichText, b::RichText) -> RichText

Concatenation. Associative, with RichText() as its identity, and normalising – so joining two runs of equal style yields one run.

source
Base.convertMethod
convert(_::Type{RichText}, s::AbstractString) -> RichText

A plain string IS a RichText of one unstyled run.

This method is what keeps label.text[] = "hi" – spelled verbatim in reactive.jl's own docstring – working on a cell that now holds a RichText: setindex!(::Reactive{T}, v) converts, and this is the conversion. Widgets that take text therefore accept either spelling without a method per widget per spelling.

source
ManyUI._rt_coalesceMethod
_rt_coalesce(
    runs::AbstractVector{TextRun}
) -> Vector{TextRun}

Drop empty runs and merge adjacent runs sharing a style.

Runs out of a wrap or a truncate arrive one grapheme at a time, so without this a 40-cell line would be 40 runs and every paint would pay 40 write_text! calls instead of two.

source
ManyUI._rt_flattenMethod
_rt_flatten(rt::RichText) -> Vector{Tuple{String, Style}}

The graphemes of rt, each paired with the style of the run it came from. The working form for wrap_width, which has to reattach styles to text the wrap has already rearranged.

source
ManyUI.plainMethod
plain(rt::RichText) -> String

The text with the styling dropped.

THE bridge to every string-shaped operation: widths, cuts and breaks are decided on this and only then carried back onto the runs, so a styled line can never measure or wrap differently from the same line unstyled.

source
ManyUI.text_widthMethod
text_width(rt::RichText) -> Int64

The width of the line in cells: the sum of its runs' widths.

Runs are segmented into graphemes INDEPENDENTLY, so a combining mark opening a run does not join the last cluster of the run before it. Splitting a cluster across two runs is a caller error – the two halves could not be given different styles on one cell anyway.

source
ManyUI.truncate_widthMethod
truncate_width(rt::RichText, w::Int64) -> RichText

The longest PREFIX of rt whose text_width is <= w, styling intact.

Stops at the first cluster that does not fit rather than skipping it: truncate_width yields a prefix, and a line that dropped a wide cluster to squeeze in the narrow one behind it would not be one. This is the rule truncate_width(::AbstractString, ::Int) already applies when it breaks out of its scan, and the two agree by construction:

plain(truncate_width(rt, w)) == truncate_width(plain(rt), w)

Pure.

source
ManyUI.wrap_widthMethod
wrap_width(rt::RichText, w::Int64) -> Vector{RichText}

Greedy word wrap to w cells, styling intact.

Wraps the PLAIN text with wrap_width(::AbstractString, ::Int) and then reattaches the styling, rather than reimplementing the greedy algorithm over styled runs. That is the whole design, and it buys the property that matters:

plain.(wrap_width(rt, w)) == wrap_width(plain(rt), w)

Colouring a paragraph cannot move one of its breaks. A second implementation of the wrap would have to be kept in step with the first forever to promise that; this one cannot drift because there is only one wrap.

Reattaching is a resynchronising walk. The wrap only ever DROPS whitespace and collapses a run of it to a single joining space; it never reorders or rewrites a visible cluster. So the output graphemes are matched against the input's in order, skipping input whitespace, and a joining space takes the style of the whitespace it stands for – that cell still has a background, and resetting it would leave a hole in a highlighted line.

source
ManyUI.CompoundSelectorType
struct CompoundSelector

Simple selectors ANDed against ONE node: Button.primary#ok.

Fields

  • parts::Vector{SimpleSelector}: Atoms that must all match the same node.
source
ManyUI.CssParseErrorType
struct CssParseError <: Exception

A stylesheet failed to parse, with the source position.

Fields

  • msg::String: What went wrong.

  • line::Int64: 1-based line.

  • col::Int64: 1-based column.

source
ManyUI.RuleType
struct Rule

One parsed rule.

Fields

  • selector::Selector: What the rule matches.

  • style::Style: Style properties it sets.

  • box::BoxPatch: Box properties it sets.

  • order::Int64: Source order; breaks specificity ties.

source
ManyUI.SelectorType
struct Selector

A full selector, read left to right; the LAST compound is the subject.

INVARIANT: length(combinators) == length(compounds) - 1.

Fields

  • compounds::Vector{CompoundSelector}: Compounds, in source order.

  • combinators::Vector{ManyUI.Combinator.T}: Relations between adjacent compounds.

source
ManyUI.SimpleSelectorType
struct SimpleSelector

One selector atom.

Fields

  • kind::ManyUI.SelectorKind.T: Universal, type, class or id.

  • name::Symbol: The name matched; ignored for UNIVERSAL.

source
ManyUI.SpecificityType
struct Specificity

The CSS specificity triple, ordered lexicographically.

Fields

  • ids::Int64: Number of id selectors.

  • classes::Int64: Number of class selectors.

  • types::Int64: Number of type selectors.

source
ManyUI.StylesheetType
struct Stylesheet

An ordered collection of rules. Immutable enough to be shared by every web session.

Fields

  • rules::Vector{Rule}: Rules, in source order.
source
ManyUI._CssCursorType
mutable struct _CssCursor

A position-tracking cursor over stylesheet source. Internal to the parser; it is the only thing that knows about lines and columns.

Fields

  • src::String

  • i::Int64: Byte index of the next character.

  • line::Int64: 1-based line of i.

  • col::Int64: 1-based column of i.

source
Base.append!Method
append!(ss::Stylesheet, rs) -> Stylesheet

Append several rules. Returns ss.

source
Base.isemptyMethod
isempty(ss::Stylesheet) -> Bool

True when the sheet has no rules. Pure.

source
Base.islessMethod
isless(a::Specificity, b::Specificity) -> Bool

Lexicographic order on (ids, classes, types). Pure.

source
Base.mergeMethod
merge(a::Stylesheet, b::Stylesheet) -> Stylesheet

Concatenate two stylesheets: b's rules come later, with their order shifted past a's. Pure.

source
Base.parseMethod
parse(_::Type{Selector}, s::AbstractString) -> Selector

Parse a single selector. Pure.

source
Base.parseMethod
parse(_::Type{Stylesheet}, s::AbstractString) -> Stylesheet

Parse a stylesheet. Pure.

source
Base.push!Method
push!(ss::Stylesheet, r::Rule) -> Stylesheet

Append a rule. Returns ss.

source
Base.showerrorMethod
showerror(io::IO, e::CssParseError)

Report the message with its line and column.

source
ManyUI._advance!Method
_advance!(c::ManyUI._CssCursor) -> Char

Consume and return the next character, tracking line and column.

source
ManyUI._borderMethod
_border(v::AbstractString) -> Border

Parse border: <kind> or border: <kind> <color>. Pure.

source
ManyUI._cascade_into!Method
_cascade_into!(
    ss::Stylesheet,
    w::Widget,
    parent_style::Style
)

Cascade w against ss under parent_style, write the result into its node, clear Dirty.STYLE, dirty it only if something actually changed, then recurse into its children so inheritance flows down.

source
ManyUI._cursorMethod
_cursor(src::AbstractString) -> ManyUI._CssCursor

A cursor at the start of src.

source
ManyUI._enumMethod
_enum(
    _::Type{E},
    v::AbstractString,
    prop::AbstractString
) -> Any

Parse a CSS keyword into the module-scoped enum E: lowercase and dashes map to the SCREAMINGSNAKECASE value name. Pure.

source
ManyUI._eofMethod
_eof(c::ManyUI._CssCursor) -> Bool

True once every character has been consumed.

source
ManyUI._errMethod
_err(c::ManyUI._CssCursor, msg::AbstractString)

Fail at the cursor's current position.

source
ManyUI._f32Method
_f32(v::AbstractString, prop::AbstractString) -> Float32

Parse a Float32 value. Pure.

source
ManyUI._intMethod
_int(v::AbstractString, prop::AbstractString) -> Int64

Parse an integer value. Pure.

source
ManyUI._is_value_errorMethod
_is_value_error(e) -> Bool

True for the exceptions a value parser raises on BAD INPUT, as opposed to the ones a bug raises. parse(Color, _), parse(Length, _) and parse_attrs signal with ArgumentError; an out-of-range channel or cell count surfaces as InexactError/OverflowError. Anything else – MethodError, UndefVarError – is a defect in a parse_property method and MUST NOT be reported to the author as "bad value". Pure.

source
ManyUI._match_fromMethod
_match_from(s::Selector, i::Int64, w::Widget) -> Bool

Match compound i of s against w, then everything to its left against w's ancestors. A DESCENDANT combinator backtracks over every ancestor; a CHILD combinator considers only the parent. Pure.

source
ManyUI._parse_compound!Method
_parse_compound!(c::ManyUI._CssCursor) -> CompoundSelector

Consume one compound selector: the atoms ANDed against a single node.

source
ManyUI._parse_declarations!Method
_parse_declarations!(
    c::ManyUI._CssCursor
) -> Tuple{Style, BoxPatch}

Consume a { ... } declaration block, folding it into one Style and one BoxPatch. Later declarations win over earlier ones.

source
ManyUI._parse_selector!Method
_parse_selector!(c::ManyUI._CssCursor) -> Selector

Consume one selector: compounds joined by > or by whitespace.

source
ManyUI._peekMethod
_peek(c::ManyUI._CssCursor) -> Char

The next character, or '\0' at end of input.

source
ManyUI._peek2Method
_peek2(c::ManyUI._CssCursor) -> Char

The character after the next one, or '\0'.

source
ManyUI._propertyMethod
_property(
    prop::AbstractString,
    value::AbstractString,
    line::Int64,
    col::Int64
) -> Tuple{Style, BoxPatch}

Dispatch one declaration through parse_property, re-throwing what it rejects as a CssParseError carrying the declaration's own line and column.

source
ManyUI._read_value!Method
_read_value!(c::ManyUI._CssCursor) -> String

Consume a declaration value: everything up to the next ; or }.

source
ManyUI._recascade_node!Method
_recascade_node!(
    ss::Stylesheet,
    w::Widget,
    parent_style::Style
)

Recascade the STYLE-dirty part of w's subtree, descending SUBTREE breadcrumbs. The breadcrumbs are left in place: relayout! reads the same trail later in the same frame.

source
ManyUI._rule_keyMethod
_rule_key(r::Rule) -> Tuple{Specificity, Int64}

The cascade sort key of a rule: (specificity, order). Pure.

source
ManyUI._skip_ws!Method
_skip_ws!(c::ManyUI._CssCursor) -> Bool

Consume whitespace and comments. Returns true when anything was consumed – which is what makes A B a descendant combinator and AB one identifier.

source
ManyUI._spacingMethod
_spacing(v::AbstractString, prop::AbstractString) -> Spacing

Parse a margin/padding shorthand of 1, 2 or 4 cell counts, in CSS order (top right bottom left). Pure.

source
ManyUI._squeezeMethod
_squeeze(v::AbstractString) -> String

Strip every space out of v, so rgb(0, 90, 180) reaches parse(Color, _) in the form it accepts. Pure.

source
ManyUI.apply_stylesheet!Method
apply_stylesheet!(ss::Stylesheet, root::Widget)

U4. The impure shell. Walks TOP-DOWN – parents before children, so inheritance flows – writes the results into each node and clears Dirty.STYLE.

Marks Dirty.LAYOUT where box actually CHANGED, and Dirty.PAINT where only computed_style changed. Compare-before-dirty is required: an unchanged cascade must cost zero frames.

source
ManyUI.cascadeFunction
cascade(ss::Stylesheet, w::Widget) -> Tuple{Style, BoxStyle}
cascade(
    ss::Stylesheet,
    w::Widget,
    parent_style::Style
) -> Tuple{Style, BoxStyle}

U4. PURE: resolve w's computed style and box. Testable with a stub widget – no App, no tree walk.

NORMATIVE per-node cascade order:

  1. style = inheritable(parent_style); box = BOX_DEFAULT
  2. fold matching_rules(ss, w) ascending by (specificity, order): style = merge(style, rule.style); box = apply(box, rule.box)
  3. style = merge(style, node(w).inline_style) and box = apply(box, node(w).inline_box) – inline ALWAYS wins.
source
ManyUI.matchesMethod
matches(c::CompoundSelector, w::Widget) -> Bool

True when every atom matches w. Pure.

source
ManyUI.matchesMethod
matches(s::Selector, w::Widget) -> Bool

True when w is the subject of s. Matched right-to-left, walking ancestors. Pure.

source
ManyUI.matchesMethod
matches(s::SimpleSelector, w::Widget) -> Bool

True when the atom matches w. Pure.

source
ManyUI.matching_rulesMethod
matching_rules(ss::Stylesheet, w::Widget) -> Vector{Rule}

Rules matching w, sorted ASCENDING by (specificity, order) – apply them in the returned order. Pure.

source
ManyUI.parse_cssMethod
parse_css(src::AbstractString) -> Stylesheet

Parse a stylesheet.

Throws CssParseError, carrying line and column, on bad input. Never throws a bare ArgumentError. Pure.

source
ManyUI.parse_propertyMethod
parse_property(
    _::Val{P},
    value::AbstractString
) -> Tuple{Style, BoxPatch}

The extensible property table – adding a property is adding a method. Used ONLY by the parser, never in a render loop. Pure.

source
ManyUI.recascade!Method
recascade!(ss::Stylesheet, root::Widget)

U4 + E1. Incremental: only STYLE-dirty subtrees, found via SUBTREE breadcrumbs. A no-op when the tree is style-clean.

source
ManyUI.specificityMethod
specificity(s::Selector) -> Specificity

Specificity of a full selector: the sum over its compounds. Pure.

source
ManyUI.@css_strMacro

Parse a stylesheet at MACRO-EXPANSION time, so a bad stylesheet is a compile error:

css"Button { color: red; }"
source