Skip to content

Paint order — layer:

The engine draws widgets in layer order, and every widget has a layer (draw_layer, default 1001). A higher number is drawn later, so it lands on top. layer: sets it from a style string.

gui_row("row-height: 6em; background: #2a4a6a; layer: 1500;")

This is opt-in and cheap to ignore. A layout that never says layer: emits exactly the same thing it always did.

What it is for

Two jobs, and it is worth knowing which one you are doing.

1. Deciding what wins when things overlap. The engine has no clip: text wider or taller than its box is drawn over its neighbours anyway (overflow: is the other half of that story). Raising a widget's layer decides which of the two stays readable.

2. Hiding a spill behind an opaque fill. This is the interesting one. A background raised above the content next to it covers that content, so an overflowing string simply disappears behind it. It is not clipping — nothing is cut — but to a player it looks the same.

# a row whose text overruns...
gui_row("row-height: 2em;")
gui_text("$text:`{a very long description}`;")

# ...and the row below hides the overflow instead of wearing it
gui_row("row-height: 8em; background: #2a4a6a; layer: 1500;")

Leave the layer: off that second row and the overflow is drawn across it, which is what always happened and still does.

Why the direction works out

Overflow only ever goes right and down — text lays out from its box's top-left. Document order also runs left-to-right, top-to-bottom. So the widget that gets damaged is always declared after the one that spills, and "later declaration, higher layer" is exactly the rule you want.

It cascades

layer: on a section or a row reaches everything inside it, the same way color and font do. So one declaration can raise a whole panel:

gui_section("area: 50,10,99,90; layer: 1500;")   # every widget in here is raised

A widget's own layer: beats the one it inherits. And a draw_layer written directly into a widget's props always wins over both — the nearest declaration is the one that counts.

The layer map

Pick numbers that do not collide with what the library already uses:

Layer Used by
1000 section / row / column backgrounds and borders (the default)
1001 the engine default — ordinary content, buttons
20000+ overlay slots

So 10029999 is yours. Above 20000 you are fighting the overlay system.

What it cannot do

It needs a colour. Hiding a spill means painting something over it, and that something has to be what should have been there. Over a solid panel that is easy. Over a 3dview or 2dview it is impossible — you would punch an opaque rectangle into the view to hide a line of text. Panels over live views have to be fixed by sizing instead.

It hides; it does not fix. The text is still wrong, it is just no longer on top of anything. spill remains the default deliberately: a visible failure gets fixed, a silent one does not.

Size the backdrop to the row or section, not to the widget. The engine draws button chrome slightly outside the rect it was given, so a fill matching a button's exact box still leaves a visible rim around it.

Gotchas

A covered widget still works. Input is not blocked by whatever is painted on top — a button under an opaque fill still takes the hover and the click. Handy, and also a hazard: do not hide a control and assume it is disabled.

Backgrounds default to 1000, which is under content. That is why a plain background: cannot hide a neighbour's overflow on its own. Raising it is the entire point of layer:.

gui_image honours it too — either in the props string (gui_image("image:smallWhite;color:#123;draw_layer:1500;")) or as a style (gui_image_stretch("smallWhite", "layer: 1500;")). An image is usually the thing doing the covering.