Mega UI

Rows and cards

How things end up where they do, and the one trick that means you never measure text.

Things stack down

Every call you make adds one thing below the last. There is no coordinate, no width and no margin to pass — the order you write is the order it appears.

csharp
ui.Header("Warehouse");
ui.Label("Everything currently in storage.");
ui.Button("Refresh", Report.Refresh);

Rows lay out sideways

Inside a Row, things sit left to right instead of stacking.

csharp
ui.Row(r =>
{
    r.Icon("package");
    r.Label("Boxes");
    r.Value(() => Warehouse.Count.ToString());
});

Spacer is the whole trick

A label on the left and a number on the right is the most-wanted layout there is, and writing it by hand means measuring the text to know where the number starts — which then breaks in German, or in Japanese, or when the number goes from 7 to 1,204.

Spacer removes the measuring entirely. Everything before it anchors to the left, everything after it to the right. Nobody measures anything and the middle can be any size.

csharp
ui.Row(r =>
{
    r.Label("Boxes in storage");
    r.Spacer();
    r.Value(() => Warehouse.Count.ToString());
});
on screen
|  Boxes in storage                    1,204  |
   \__ anchored left        anchored right __/

You can put a Spacer anywhere, including first — that pushes the whole row to the right edge.

Cards group things

A Card is a panel with its own background. It sizes itself to whatever you put inside, and everything works in there: rows, headers, even another card.

csharp
ui.Card(c =>
{
    c.Header("Today");

    c.Row(r =>
    {
        r.Label("Sold");
        r.Spacer();
        r.Value(() => Day.Sold.ToString());
    });

    c.Row(r =>
    {
        r.Label("Restocked");
        r.Spacer();
        r.Value(() => Day.Restocked.ToString());
    });
});

Columns split it sideways

Where a Row is one line, Columns is two or more stacks beside each other. Each column is a full builder, and they all end up as tall as the tallest.

csharp
ui.Columns(
    left =>
    {
        left.Header("Stock");
        left.Row(r => { r.Label("Boxes"); r.Spacer(); r.Value(() => "..."); });
    },
    right =>
    {
        right.Header("Staff");
        right.Row(r => { r.Label("On shift"); r.Spacer(); r.Value(() => "..."); });
    });

Long screens scroll by themselves

There is no Scrollable option, and that is deliberate. Whether a screen fits depends on the shape of the player's monitor and on their language — the same screen fits in English and overflows in German, where the words are longer and one label wraps onto an extra line.

Nobody wins that guess while writing the mod. The library measures the real window, compares it to the real height of what you built, and turns scrolling on only when it is needed. The bar floats over the content rather than reserving a strip, so nothing reflows when it appears.

It always opens at the top

A window remembers nothing about where it was scrolled to. Reopening halfway down a list reads as a bug — the player does not remember scrolling, sees a cut-off header, and concludes the screen opened wrong.

For a drawing that is bigger than the window on both axes — a tree, a floor plan — you want a region instead, which the player drags and zooms.

Sizing something to the window

ui.Area is the visible area in pixels. It is what you need when a widget should fill what is left rather than a number you guessed.

csharp
ui.Diagram(ui.Area.y, DrawEverything, bleed: true);

Separators and gaps

CallWhat you get
ui.Separator()A horizontal line, with breathing room either side
ui.Gap()Blank vertical space — 12 pixels, or pass your own
r.Gap()The same, sideways, inside a row — 8 pixels by default

What the layout deliberately does not do

There is no flex, no weight, no grid and no second measuring pass. Position is accumulated, not solved: each widget knows its own height, and the running total is the height of your screen.

It is the dumbest model that covers the job, and that is on purpose. A real layout engine would solve screens nobody is going to build inside a shop monitor, and it would buy an edge-case bug that nobody can debug from in there.

Why not VerticalLayoutGroup?

Unity ships one and it nearly works. Two problems: it runs at the end of the frame, so reading your height right after building returns zero and the window is the wrong size until the next frame; and it fights the scale bridge that maps pixels onto the computer's world-space units. Positioning by hand costs one file and gives a correct height in the same frame.