Brodgar client

hafen.ui: Which Surfaces a Rule Reaches

A sheet key is a selector resolved one of two ways: a site key names a place the client draws and is resolved there. Any other valid selector is a tree key and says which widgets to style.

local sheet = hafen.ui():sheet()
local body = hafen.font():get("serif"):derive():size(12)
sheet:rule("*"):font(body)                                    -- what kind of surface (site key)
sheet:rule("window[title=Cupboard]"):color{200, 180, 140}     -- which widgets (tree key)
sheet:install()
Rule Detail
Which kind a key is A bare role is a site key. A role with a refiner, or a role with no site behind it (window, inventory, item, fight.cooldown), is a tree key. The key’s shape decides.
Invalid grammar Raises, exactly as session:ui():match(selector) does.

Site keys

Key What it styles
* The global fallback: most UI text, and the cascade for every rule you do not write.
window.title Window captions, and the plate they sit on.
window.frame The window chrome: the frame, the surface it sits on, and where its ornaments go. Draws no text: takes bg, border, padding and the ornament properties.
panel The window-less framed surfaces (panels). Boxes around lists and info panes, the HUD portrait, party avatars, flower-menu petals, dropdown menus, the AddOns manager’s Browse cards and screenshot box.
heading In-window section headings, the embossed fraktur ones.
button Standard buttons: their captions and the face they are drawn on — fill, frame, and the fill’s state faces.
label Body text: attribute rows, list items, explicit-foundry labels.
textentry Text-entry fields and the console command line: the letters and the field they are typed into.
tooltip Every tooltip — items, buffs, meters, craft, minimap, the action menu — and the box it pops up in.
inventory.slot The empty square an inventory grid and the equipment window are tiled with. bg and border.
checkbox The box a checkbox ticks (its caption is label’s). bg and border.
checkbox.mark The tick inside that box, drawn while ticked.
scrollbar, scrollbar.knob The rail a scroll thumb runs down, and the thumb.
slider, slider.knob The rail a slider’s thumb runs along, and the thumb.
meter A bar the game fills: what its fill is drawn on, and the frame round it. The fill stays the server’s.
hud.belt The plate under the numbered belt squares — the HUD’s plates.
hud.menu.left, hud.menu.right The plates behind the toggle buttons in the bottom corners.
hud.search The plate the action-search button sits on.
minimap.frame The frame around the corner minimap.
menu Flower-menu petals and the action-menu keybind letters.
menu.slot The empty square the action menu’s grid is tiled with. bg and border.
menu.frame The frame around that grid, minimap.frame’s twin, dressed with picture.
chat The chat window — messages, channel tabs, the typed line — and the cascade for the kinds below.
chat.frame The chat’s own decoration: the field it tiles behind everything and the frame round it — window.frame’s counterpart.
chat.log The wash one channel lays behind its lines, inside that frame. Draws no text.
chat.system The System log’s lines.
chat.mine Your own line, in any channel.
chat.private A private message, received or sent.
chat.party A line in the Party channel.
chat.urgent The unread indicator: a waiting channel’s tab, the glow on the chat button.
chat.speaker The colour a speaker is given in a multi-person channel.
world.nick Floating kin names over characters.
world.speech Speech bubbles.
Rule Detail
Stock stays per surface Each surface keeps its own stock size and colour unless a rule overrides them. textentry fronts the serif fields and the mono command line, and both stay native under one rule.
The chat. keys refine chat, not * chat.system, chat.mine, chat.private, chat.party, chat.urgent and chat.speaker fall back to chat first. Nothing else cascades through a dot. chat.frame, chat.log, checkbox.mark, the two knobs, menu.slot and menu.frame are parts, and take nothing from the whole. Name both, or the one left out stays the client’s.

Tree keys

Any other valid selector — @Class, window[title=…], [text=…], [res=…], a chain, or a role classifying a widget (window, inventory, item, a region’s such as fight.cooldown). Every tree rule matching a widget is folded into one style. widget:style() reads the result, nil when nothing names it.

hafen.ui():sheet():rule("window[title=Cupboard]"):color{200, 180, 140}:sheet():install()
local session = hafen.session():current()
session:ui():match("window[title=Cupboard]"):style()    -- { color = {r=200, g=180, b=140, a=255} }
session:ui():inventory():style()                       -- nil
Rule Detail
The whole subtree Parents draw before children. A tree rule is in force for the matched widget’s caption, labels, button captions, rows and widgets created inside it later. Text drawn by the game’s own resource code is included.
A window’s frame No role names the decoration (a child of the window with no role), so window.frame names the site. A ["window…"] tree rule still reaches both the text inside and the chrome, because the decoration asks the window what it resolved.
The more specific rule wins, per property Specificity is the selector’s parts added up — role 1, @Class 2, [title=]/[text=] 4, [res=] 8, [name=] 16 — a chain summing every step. A specific rule setting only color does not take the font a broader one set. Equal specificity goes to the rule applied last, addons included. Applied means when the sheet was first installed, so editing one rule never promotes a sheet.
A chain reaches everything inside window[title=Cupboard] * matches every widget below that window. window[title=Cupboard] names the window alone. A chain follows a rename: a caption change re-resolves everything below it.
A site key is not a widget’s style * and the site keys resolve where they draw. widget:style() never reports one.
Tree over site Where both reach the same text, the tree rule wins for the properties it names and the site rule fills the rest. The example above paints one window’s text in body, in tan, and every other window in body in its own colour.

What each key accepts

Every drawing property is accepted on every key. What differs is what the surface does with it. Two exceptions raise: position, anchor, size and margin on a site key, and a flat color on chat.urgent or chat.speaker, which take a sequence.

Text

Key font color Notes
* Yes Yes Cascades to every key you do not write.
window.title Yes With emboss(false) Embossed: a texture is tiled through the glyph mask, so nothing tints until the relief is dropped.
heading Yes With emboss(false) Embossed. Two stock sizes use this key. A size-less rule keeps each.
button Yes Partly The ordinary caption is embossed (needs emboss(false)). A wrapped caption, or one the client sets with a colour, follows color as it stands. Stock is bold serif 12.
label Yes Yes A larger size clips: row heights were measured at construction.
textentry Yes Yes A larger size clips. A field’s height comes from its background, and a bg is that background. The art you give is the height of a field built afterwards.
tooltip Yes Yes $col[…] rows keep their colour. size is safe, a tip sizing its box around its text.
menu Yes Yes size is safe: a petal re-sizes around its centre.
chat Yes Yes One rule paints every kind of line alike.
chat.system, chat.mine, chat.private, chat.party Yes Yes One kind each, falling back to chat, then *. $col[…] in the line wins.
chat.urgent, chat.speaker Inert A sequence Neither draws text nor holds one colour: each takes the sequence it hands out. A flat color raises.
world.nick Yes Yes A color rule flattens the kin-group colours. A font-only rule leaves them.
world.speech Yes Inert The bubble blits its text under a flat black tint. size is safe.
Any tree key, widget:rule() Yes Per surface Resolved per widget over its subtree, carrying the caveats above: a rule on a window covers its caption, where color waits on emboss. :style() reports a colour a surface then throws away.

Carved surfaces

emboss reaches the keys the client renders as a mask and fills with a picture. glow reaches the halo behind them. Independent: a key may carry either alone.

Key emboss glow Notes
window.title Yes Yes The caption, in the theme’s texture or the rule’s flat color, on the theme’s halo. The plate behind it is bg on the same key.
heading Yes Yes Both sizes of section heading.
button Yes Yes The ordinary caption. A wrapped one was never carved.
* Cascades Cascades Into those three alone: one rule on * flattens every carved caption.
A tree key, widget:rule() Yes Yes Per widget: ["window[title=Inventory]"] flattens one window’s caption.
Every other key Inert Inert Readable through :style().

A window’s caption halo differs focused and unfocused. A glow rule replaces both.

Chrome

Honoured by the surfaces that draw a box of their own. A state face inside a bg is used by the surfaces that have that state.

Key bg border padding Notes
window.frame Yes Yes Yes Every window wearing the stock decoration. The one surface where padding and a border’s insets move anything, since a window re-lays itself out.
window.title Yes Yes Inert Paint the caption plate at the box the client sizes around the caption. The caption’s place is window.frame’s caption.
panel, boxed Inert Yes Inert List and info boxes, the HUD portrait, party avatars, the map’s view and marker list. A border around content that is not the panel’s (why).
panel, self-painting Yes Yes Inert Flower-menu petals, dropdown menus, an item-stock box, a Browse card, the screenshot box.
tooltip Yes Yes Yes The box a tip pops up in. padding is the room between text and edge, the box growing outward. Without bg or border the client’s dark fill and yellow outline stay.
inventory.slot Yes Yes Inert One grid square at the client’s size and pitch — the square.
button Yes Yes Inert The face: bg stands in for the fill, border for the four edge caps. Either alone leaves the other. Its box was fixed when built — the face.
textentry Yes Yes Yes The field (the field). bg is the stretched middle, border the two end caps. padding is the room between those and the text, inside a width the caller owns.
menu.slot Yes Yes Inert One action-menu cell, the same raster as inventory.slot under its own key.
chat.frame Yes Yes Inert bg the field tiled inside the margin, border the frame. The client’s frame has no bottom edge, so art wants a transparent bottom slice — the chat.
chat.log Yes Yes Inert The wash behind a channel’s lines. The client paints no frame there, so a border adds one.
meter Yes Yes Inert bg is what the fill is drawn on (the trough of a horizontal bar, the whole box of a vertical one). border frames the bar. The fill is the server’s, one per meter.
checkbox, scrollbar, slider Yes Yes Inert The box and the two rails, over the rectangle the control was built with. A checked face in a checkbox bg is what a ticked box draws. A checkbox you built and disabled wears disabled. A checkbox drawn as one picture (the HUD’s map and menu buttons, a dropdown’s arrow) is not in this key — the three controls.
checkbox.mark, scrollbar.knob, slider.knob Yes Yes Inert The tick and the two thumbs, each dressed apart from its whole.
world.speech Yes Yes Inert The bubble: bg its white fill, border its frame — the bubble.
* Cascades Cascades Cascades Into every row above, each under its own row. Text-only surfaces ignore it.
The HUD plate keys Inert Inert Inert A plate is a whole picture: picture dresses it.
Every other site key Inert Inert Inert A text site has no surface to paint.
A tree key matching a window Yes Yes Yes That window’s chrome.
A tree key matching a panel Per panel Yes Inert ["@Frame"] or ["window[title=…] @Frame"] themes those panels alone.
A tree key matching a button or field Yes Yes Per surface ["@Button"], ["window[title=…] @TextEntry"]. A field’s height is read where it is built, from the site half alone.
A tree key matching a widget an addon built Yes Yes On a column Applied over the widget’s own stock: bg under the addon’s Draw, border over it. padding is the room inside a column or row.
A tree key matching anything else Inert Inert Inert Readable through widget:style().
widget:rule() Per surface Per surface Per surface As the rows above, one widget at a time.

A site key does not compose with * per property. Within the site half a key has a rule of its own or falls back to * whole. So ["*"] = {bg = …} beside ["window.frame"] = {border = …} gives the border alone. Write both properties in the rule naming the surface. Levels above the site half do compose per property.

Ornaments

Key caption sizer closeButton Notes
window.frame Yes Yes Yes Where the decoration puts the caption, and the art and corner of the two ornaments. A window that draws no sizer ignores that one.
Every other key, * included Inert Inert Inert Readable through :style().

Picture

picture reaches the places of the client’s own that blit a picture, and the pictures the server places. They are told apart by where they sit in the tree.

Key picture Notes
The HUD plate keys Yes The rule’s picture fills the rectangle the client had. Nothing moves and every button keeps its click.
menu.frame Yes The grid is drawn over it, so art with no hole hides the actions.
meter Yes The frame a bar blits round itself, over the fill on a horizontal bar, under it on a vertical one. On the horizontal one the art is the server’s, one per meter, so give a single picture a centre the fill shows through.
* Cascades Into the plates, menu.frame and meter alone: a picture on * paints the whole HUD, the action menu’s frame and every meter alike.
A tree key matching a picture Yes ["@Img"], or a chain naming its window. A state face inside is used by a surface entering that state. A picture the server re-points keeps following the rule.
widget:rule() Yes One picture at a time.
Every other key Inert A text site draws no picture. A boxed surface takes bg and border.

Layout

Key position / anchor size margin Notes
Any tree key Unless a column places it Unless the widget owns its size Inside a column The matched widget is moved for real, the field a drag writes. A child a column lays out has no place of its own and a column’s box is its content’s: inert, never an error. A window that packs around its contents re-packs, inert the same way. margin is honoured on a column’s child and inert on a widget placed by hand.
Any site key, * included Raises Raises Raises A site is where text is drawn. The error names the fix: select the widget.
widget:rule() Raises Raises Yes The hand-named level of a place is widget:position(x, y). No verb spells a margin, so this level is its hand-named one, as for padding. A :stock takes it too.
Structural limit Detail
Where color is inert The glyph colour is thrown away before the screen: embossed surfaces until emboss(false). The speech bubble always. font still applies.
Text rasterised at class-load A static field’s text never follows a rule. The one such row the client ships is reached by carrying a local copy of that resource’s code — surfaces.
$col[…] markup Wins over a color rule: it is part of the string.
A rule flattens meaning While ["*"] = {color = …} is on, a red warning is the same colour as everything else. Style one key when that matters.
Geometry Only padding moves anything, where a surface owns its layout. A larger font size clips where a box was measured from the stock font — surfaces.

See Also