Color, style and CSS
ManyUI.ANSI16_PALETTE — Constant
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.
ManyUI.ANSI256_PALETTE — Constant
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].
ManyUI.CUBE_LEVELS — Constant
The six per-channel levels of the 6x6x6 cube: 0, 95, 135, 175, 215,
[I].
ManyUI.NAMED_COLORS — Constant
The color names parse(Color, s) and color(name) accept. [I].
ManyUI.Color — Type
struct ColorA 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 readr,g,b.r::UInt8: Red channel, or the palette index for ANSI16/ANSI256.g::UInt8: Green channel.b::UInt8: Blue channel.
Base.parse — Method
parse(_::Type{Color}, s::AbstractString) -> Color
Parse a color, throwing ArgumentError on garbage. Pure.
Base.tryparse — Method
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.
ManyUI._linearize — Method
_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.
ManyUI.ansi16 — Method
ansi16(i::Integer) -> Color
A 16-color palette entry, 0 <= i <= 15. Throws ArgumentError otherwise. Pure.
ManyUI.ansi256 — Method
ansi256(i::Integer) -> Color
A 256-color palette entry, 0 <= i <= 255. Throws ArgumentError otherwise. Pure.
ManyUI.ansi256_to_ansi16 — Method
ansi256_to_ansi16(c::Color) -> Color
Nearest 16-color entry. Pure.
ManyUI.color — Method
color(name::Symbol) -> Color
Look up a named color, e.g. color(:bright_black). Throws KeyError when unknown. Pure.
ManyUI.color_distance — Method
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.
ManyUI.color_index — Method
color_index(c::Color) -> UInt8
Palette index of an ANSI16/ANSI256 color. Pure.
ManyUI.degrade — Method
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 \ depth | TRUECOLOR | ANSI256 | ANSI16 | MONOCHROME |
|---|---|---|---|---|
RGB | identity | rgb_to_ansi256 | rgb_to_ansi16 | lum >= 0.5 ? 15 : 0 |
ANSI256 | identity | identity | ansi256_to_ansi16 | as above |
ANSI16 | identity | identity | identity | idx in (7, 15) ? 15 : 0 |
DEFAULT | identity | identity | identity | identity |
UNSET | identity | identity | identity | identity |
ManyUI.detect_color_depth — Function
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 -> ANSI16ManyUI.is_set — Method
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.
ManyUI.is_token — Method
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.
ManyUI.is_unset — Method
is_unset(c::Color) -> Bool
True when c specifies nothing and must inherit. Pure.
ManyUI.luminance — Method
luminance(c::Color) -> Float64
Relative luminance in 0..1, computed from sRGB-decoded (linear) channels. Pure.
ManyUI.rgb — Method
rgb(r::Integer, g::Integer, b::Integer) -> Color
A TrueColor value from three 0:255 channels. Pure.
ManyUI.rgb — Method
rgb(hex::Integer) -> Color
A TrueColor value from a packed hex literal, e.g. rgb(0xff8800). Accepts 0 <= hex <= 0xffffff. Pure.
ManyUI.rgb_to_ansi16 — Method
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.
ManyUI.rgb_to_ansi256 — Method
rgb_to_ansi256(c::Color) -> Color
Nearest 256-color entry: the 6x6x6 cube, the 24 greys, and the 16 system colors. Pure.
ManyUI.to_rgb — Method
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.
ManyUI.AttrMask — Type
Bitset over Attr.T values.
ManyUI.Style — Type
struct StyleA 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_UNSETmeans inherit.bg::Color: Background color;COLOR_UNSETmeans 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.
ManyUI.Style — Method
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.
Base.merge — Method
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.
ManyUI.degrade — Method
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.
ManyUI.has — Method
has(s::Style, a::ManyUI.Attr.T) -> Bool
True when a is both specified AND on. Pure.
ManyUI.inheritable — Method
inheritable(s::Style) -> Style
The inheritable subset of s: the foreground and all text attributes. NOT the background. Pure.
ManyUI.parse_attrs — Method
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.
ManyUI.resolve — Method
resolve(s::Style) -> Style
Replace UNSET colors with DEFAULT. Call once, at the root, before emission. Pure.
ManyUI.specified — Method
specified(s::Style, a::ManyUI.Attr.T) -> Bool
True when a is specified at all, on or off. Pure.
ManyUI.with — Method
with(s::Style, a::ManyUI.Attr.T, on::Bool) -> Style
Copy of s with a specified and set to on. Pure.
ManyUI.without — Method
without(s::Style, a::ManyUI.Attr.T) -> Style
Copy of s with a unspecified – clears both the value and the mask. Pure.
ManyUI._TOKEN_FALLBACK — Constant
The colour a token falls back to when the current theme does not name it, indexed by id.
ManyUI._TOKEN_IDS — Constant
Name to id.
ManyUI._TOKEN_NAMES — Constant
Token names, indexed by id. id = i is _TOKEN_NAMES[i].
ManyUI.Theme — Type
struct ThemeA 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.
ManyUI.register_theme! — Method
register_theme!(th::Theme) -> Theme
Make th askable for by name. Replaces a theme of the same name.
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.
ManyUI.resolve_token — Function
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.
ManyUI.resolve_token — Function
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.
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.
ManyUI.theme — Method
theme() -> Theme
The theme in force, or the registered theme called name.
ManyUI.theme_color — Method
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.
ManyUI.themes — Method
themes() -> Vector{Symbol}
Every registered theme name, sorted.
ManyUI.token — Method
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.
ManyUI.token_name — Method
token_name(c::Color) -> Symbol
The name a token Color carries. Throws for anything else.
ManyUI.token_names — Method
token_names() -> Vector{Symbol}
Every declared token name, sorted.
ManyUI.RICHTEXT_EMPTY — Constant
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.
ManyUI.TextLike — Type
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.
ManyUI.RichText — Type
struct RichTextA 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.
ManyUI.RichText — Type
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.
ManyUI.RichText — Method
RichText(runs::TextRun...) -> RichText
A RichText of the given runs, in order.
ManyUI.TextRun — Type
struct TextRunA 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;RichTextdrops such runs.style::Style: The override, folded over the widget's style at paint time.
Base.convert — Method
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.
ManyUI._rt_coalesce — Method
_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.
ManyUI._rt_flatten — Method
_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.
ManyUI._rt_is_space — Method
_rt_is_space(g::AbstractString) -> Bool
True when g, a single grapheme, is whitespace.
ManyUI.plain — Method
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.
ManyUI.text_width — Method
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.
ManyUI.truncate_width — Method
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.
ManyUI.wrap_width — Method
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.
ManyUI.CompoundSelector — Type
struct CompoundSelectorSimple selectors ANDed against ONE node: Button.primary#ok.
Fields
parts::Vector{SimpleSelector}: Atoms that must all match the same node.
ManyUI.CssParseError — Type
struct CssParseError <: ExceptionA stylesheet failed to parse, with the source position.
Fields
msg::String: What went wrong.line::Int64: 1-based line.col::Int64: 1-based column.
ManyUI.Rule — Type
struct RuleOne 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.
ManyUI.Selector — Type
struct SelectorA 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.
ManyUI.SimpleSelector — Type
struct SimpleSelectorOne selector atom.
Fields
kind::ManyUI.SelectorKind.T: Universal, type, class or id.name::Symbol: The name matched; ignored forUNIVERSAL.
ManyUI.Specificity — Type
struct SpecificityThe CSS specificity triple, ordered lexicographically.
Fields
ids::Int64: Number of id selectors.classes::Int64: Number of class selectors.types::Int64: Number of type selectors.
ManyUI.Stylesheet — Type
struct StylesheetAn ordered collection of rules. Immutable enough to be shared by every web session.
Fields
rules::Vector{Rule}: Rules, in source order.
ManyUI.Stylesheet — Method
Stylesheet() -> Stylesheet
An empty stylesheet.
ManyUI._CssCursor — Type
mutable struct _CssCursorA position-tracking cursor over stylesheet source. Internal to the parser; it is the only thing that knows about lines and columns.
Fields
src::Stringi::Int64: Byte index of the next character.line::Int64: 1-based line ofi.col::Int64: 1-based column ofi.
Base.append! — Method
append!(ss::Stylesheet, rs) -> Stylesheet
Append several rules. Returns ss.
Base.isempty — Method
isempty(ss::Stylesheet) -> Bool
True when the sheet has no rules. Pure.
Base.isless — Method
isless(a::Specificity, b::Specificity) -> Bool
Lexicographic order on (ids, classes, types). Pure.
Base.merge — Method
merge(a::Stylesheet, b::Stylesheet) -> Stylesheet
Concatenate two stylesheets: b's rules come later, with their order shifted past a's. Pure.
Base.parse — Method
parse(_::Type{Selector}, s::AbstractString) -> Selector
Parse a single selector. Pure.
Base.parse — Method
parse(_::Type{Stylesheet}, s::AbstractString) -> Stylesheet
Parse a stylesheet. Pure.
Base.push! — Method
push!(ss::Stylesheet, r::Rule) -> Stylesheet
Append a rule. Returns ss.
Base.showerror — Method
showerror(io::IO, e::CssParseError)
Report the message with its line and column.
ManyUI._advance! — Method
_advance!(c::ManyUI._CssCursor) -> Char
Consume and return the next character, tracking line and column.
ManyUI._border — Method
_border(v::AbstractString) -> Border
Parse border: <kind> or border: <kind> <color>. Pure.
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.
ManyUI._color — Method
_color(v::AbstractString) -> Color
Parse a color value. Pure.
ManyUI._cursor — Method
_cursor(src::AbstractString) -> ManyUI._CssCursor
A cursor at the start of src.
ManyUI._enum — Method
_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.
ManyUI._eof — Method
_eof(c::ManyUI._CssCursor) -> Bool
True once every character has been consumed.
ManyUI._err — Method
_err(c::ManyUI._CssCursor, msg::AbstractString)
Fail at the cursor's current position.
ManyUI._f32 — Method
_f32(v::AbstractString, prop::AbstractString) -> Float32
Parse a Float32 value. Pure.
ManyUI._int — Method
_int(v::AbstractString, prop::AbstractString) -> Int64
Parse an integer value. Pure.
ManyUI._is_ident_char — Method
_is_ident_char(ch::Char) -> Bool
True when ch may continue an identifier.
ManyUI._is_ident_start — Method
_is_ident_start(ch::Char) -> Bool
True when ch may open an identifier.
ManyUI._is_simple_start — Method
_is_simple_start(ch::Char) -> Bool
True when ch may open a simple selector.
ManyUI._is_value_error — Method
_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.
ManyUI._match_from — Method
_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.
ManyUI._parse_compound! — Method
_parse_compound!(c::ManyUI._CssCursor) -> CompoundSelector
Consume one compound selector: the atoms ANDed against a single node.
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.
ManyUI._parse_selector! — Method
_parse_selector!(c::ManyUI._CssCursor) -> Selector
Consume one selector: compounds joined by > or by whitespace.
ManyUI._peek — Method
_peek(c::ManyUI._CssCursor) -> Char
The next character, or '\0' at end of input.
ManyUI._peek2 — Method
_peek2(c::ManyUI._CssCursor) -> Char
The character after the next one, or '\0'.
ManyUI._property — Method
_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.
ManyUI._read_ident! — Method
_read_ident!(c::ManyUI._CssCursor) -> String
Consume one identifier.
ManyUI._read_value! — Method
_read_value!(c::ManyUI._CssCursor) -> String
Consume a declaration value: everything up to the next ; or }.
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.
ManyUI._rule_key — Method
_rule_key(r::Rule) -> Tuple{Specificity, Int64}
The cascade sort key of a rule: (specificity, order). Pure.
ManyUI._skip_comment! — Method
_skip_comment!(c::ManyUI._CssCursor)
Consume a /* ... */ comment. The cursor must sit on its /.
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.
ManyUI._spacing — Method
_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.
ManyUI._squeeze — Method
_squeeze(v::AbstractString) -> String
Strip every space out of v, so rgb(0, 90, 180) reaches parse(Color, _) in the form it accepts. Pure.
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.
ManyUI.cascade — Function
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:
style = inheritable(parent_style);box = BOX_DEFAULT- fold
matching_rules(ss, w)ascending by(specificity, order):style = merge(style, rule.style);box = apply(box, rule.box) style = merge(style, node(w).inline_style)andbox = apply(box, node(w).inline_box)– inline ALWAYS wins.
ManyUI.matches — Method
matches(c::CompoundSelector, w::Widget) -> Bool
True when every atom matches w. Pure.
ManyUI.matches — Method
matches(s::Selector, w::Widget) -> Bool
True when w is the subject of s. Matched right-to-left, walking ancestors. Pure.
ManyUI.matches — Method
matches(s::SimpleSelector, w::Widget) -> Bool
True when the atom matches w. Pure.
ManyUI.matching_rules — Method
matching_rules(ss::Stylesheet, w::Widget) -> Vector{Rule}
Rules matching w, sorted ASCENDING by (specificity, order) – apply them in the returned order. Pure.
ManyUI.parse_css — Method
parse_css(src::AbstractString) -> Stylesheet
Parse a stylesheet.
Throws CssParseError, carrying line and column, on bad input. Never throws a bare ArgumentError. Pure.
ManyUI.parse_property — Method
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.
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.
ManyUI.specificity — Method
specificity(c::CompoundSelector) -> Specificity
Specificity of one compound. Pure.
ManyUI.specificity — Method
specificity(s::Selector) -> Specificity
Specificity of a full selector: the sum over its compounds. Pure.
ManyUI.@css_str — Macro
Parse a stylesheet at MACRO-EXPANSION time, so a bad stylesheet is a compile error:
css"Button { color: red; }"