Skip to content

Cookbook

This page collects short Tick UI recipes for patterns that come up across most projects. Each recipe is one task plus a code sample. For the model behind these patterns, refer to the topic pages they cross-link to.

A few recipes use Color.WithAlpha, an extension method on UnityEngine.Color shipped with the package. It returns a copy of the colour with the alpha channel replaced; RGB is untouched.

Layout

Title bar plus content

A fixed-height title strip on top, the rest for content.

ui.PushSizedVertical(new Size[] { 32, 1.0f });
{
    ui.FillRounded(headerColor, 4);
    ui.Text("Settings", "roboto_mono", 16, Color.white,
        TextAlign.MiddleLeft);
    ui.Next();

    ui.PushColor(bodyColor);
    ui.Fill();
    ui.PopColor();
}
ui.Pop();

For the schema, refer to Layout schemas.

Three rows: fixed header, fill body, fixed footer.

ui.PushSizedVertical(new Size[] { 48, 1.0f, 32 });
{
    DrawHeader(ui); ui.Next();
    DrawBody(ui);   ui.Next();
    DrawFooter(ui);
}
ui.Pop();

A fixed-width sidebar on the left, the rest for the main area.

ui.PushSizedHorizontal(new Size[] { 220, 1.0f });
{
    DrawSidebar(ui); ui.Next();
    DrawMainArea(ui);
}
ui.Pop();

Centered modal content

Content centered horizontally and vertically in the current rect, to a fixed maximum size.

using (ui.Auto().Center(480, 280))
{
    ui.FillRounded(modalColor, 8);
    using (ui.Auto().Pad(16))
    {
        DrawModalBody(ui);
    }
}

Toolbar of equal buttons

A horizontal row of equal-sized buttons with a consistent gap.

using (ui.Auto().Top(36))
{
    ui.PushSeriesHorizontal(80, 6);
    {
        DrawTool(ui, "select"); ui.Next();
        DrawTool(ui, "move");   ui.Next();
        DrawTool(ui, "rotate"); ui.Next();
        DrawTool(ui, "scale");
    }
    ui.Pop();
}

Bottom-anchored status bar

A fixed-height strip pinned to the bottom; the rest is content.

ui.PushSizedVertical(new Size[] { 1.0f, 24 });
{
    DrawContent(ui); ui.Next();

    ui.Fill(statusColor);
    ui.Text(statusText, "roboto_mono", 12, Color.white,
        TextAlign.MiddleLeft);
}
ui.Pop();

Widgets

Labelled textbox

A field with a label on the left and the input on the right.

using (ui.Auto().Top(28))
{
    ui.PushSizedHorizontal(new Size[] { 100, 1.0f });
    {
        ui.Text("Username", "roboto_mono", 14, textColor,
            TextAlign.MiddleLeft);
        ui.Next();

        var config = TextboxConfig.Default;
        config.Font = "roboto_mono";
        config.FontSize = 14;
        ui.Textbox("username", ref m_username, config,
            out TextboxLayout layout);

        ui.FillRounded(fieldColor, 4);
        ui.PushRelativeRect(layout.TextRect);
            ui.Text(layout.RenderText, config.Font, config.FontSize,
                textColor, TextAlign.MiddleLeft);
        ui.Pop();
    }
    ui.Pop();
}

For the full pattern, refer to Make a textbox.

Confirm and cancel row

A right-aligned button pair at the bottom of a form.

using (ui.Auto().Bottom(40).PadHorizontal(8))
{
    using (ui.Auto().Right(80))
    {
        ui.FillRounded(primaryColor, 4);
        ui.Text("Confirm", "roboto_mono", 14, Color.white,
            TextAlign.MiddleCenter);
        if (ui.Button("confirm")) Confirm();
    }
    using (ui.Auto().Right(160).Left(80))
    {
        ui.FillRounded(secondaryColor, 4);
        ui.Text("Cancel", "roboto_mono", 14, Color.white,
            TextAlign.MiddleCenter);
        if (ui.Button("cancel")) Cancel();
    }
}

Toggle button

A button that flips a bound bool and reflects its current state in the visual.

ui.FillRounded(m_muted ? accentColor : neutralColor, 4);
ui.Text(m_muted ? "Muted" : "Mute", "roboto_mono", 14,
    Color.white, TextAlign.MiddleCenter);
if (ui.Button("mute"))
    m_muted = !m_muted;

Numeric stepper

A value with - and + buttons on either side.

ui.PushSizedHorizontal(new Size[] { 28, 1.0f, 28 });
{
    if (ui.Button("dec")) m_count = Math.Max(0, m_count - 1);
    ui.Text("-", "roboto_mono", 16, Color.white, TextAlign.MiddleCenter);
    ui.Next();

    ui.Text(m_count.ToString(), "roboto_mono", 14, textColor,
        TextAlign.MiddleCenter);
    ui.Next();

    if (ui.Button("inc")) m_count++;
    ui.Text("+", "roboto_mono", 16, Color.white, TextAlign.MiddleCenter);
}
ui.Pop();

Panels and overlays

A blocking dialog with two actions; closes itself on either.

if (ui.BeginPanel("confirm-delete",
    new Rect(0, 0, Screen.width, Screen.height),
    new PanelConfig
    {
        Hidden = true,
        BackdropColor = new Color(0, 0, 0, 0.6f),
    }))
{
    using (ui.Auto().Center(360, 160))
    {
        ui.FillRounded(modalColor, 8);
        using (ui.Auto().Pad(16))
        {
            ui.PushSizedVertical(new Size[] { 1.0f, 32 });
            {
                ui.Text("Delete this item?", "roboto_mono", 16,
                    Color.white, TextAlign.TopLeft);
                ui.Next();

                if (ui.Button("ok"))     { Delete(); ui.HidePanel("confirm-delete"); }
                if (ui.Button("cancel")) ui.HidePanel("confirm-delete");
            }
            ui.Pop();
        }
    }

    ui.EndPanel();
}

// Open with ShowModal — sets BlocksInput so input is blocked below.
if (m_userPressedDelete)
    ui.ShowModal("confirm-delete");

For the full pattern, refer to Show a modal.

Right-click context menu

A popup that appears at the cursor on right-click and dismisses itself on click-outside.

if (ui.IsHovered() && Input.GetMouseButtonDown(1))
    ui.ShowPopup("context-menu",
        new Vector2(mouseX, mouseY));

if (ui.BeginPopup("context-menu", new Rect(0, 0, 200, 120), hidden: true))
{
    using (ui.Auto().Pad(4))
    {
        DrawMenuItem(ui, "open", "Open");
        DrawMenuItem(ui, "copy", "Copy");
        DrawMenuItem(ui, "delete", "Delete");
    }
    ui.EndPopup();
}

BeginPopup wraps BeginPanel with AutoCloseOnOutsideClick = true, so the panel hides itself automatically when input lands elsewhere.

Tabs in a panel

A row of tab buttons drives a switch over the body content.

ui.PushSizedVertical(new Size[] { 32, 1.0f });
{
    ui.PushSplitHorizontal(3);
    {
        DrawTab(ui, Tab.General); ui.Next();
        DrawTab(ui, Tab.Audio);   ui.Next();
        DrawTab(ui, Tab.Video);
    }
    ui.Pop();

    ui.Next();

    switch (m_activeTab)
    {
        case Tab.General: DrawGeneralPanel(ui); break;
        case Tab.Audio:   DrawAudioPanel(ui);   break;
        case Tab.Video:   DrawVideoPanel(ui);   break;
    }
}
ui.Pop();

Scroll views

Scrollable log with auto-scroll-to-bottom

A vertical scroll view that snaps to the bottom whenever the underlying log gains a line.

if (m_logCountThisFrame > m_lastLogCount)
    ui.ScrollToBottom("log");
m_lastLogCount = m_logCountThisFrame;

ScrollView.Begin(ui, "log",
    new Vector2Int(contentW, totalHeight),
    ScrollView.s_scrollConfigVertical);
{
    ui.Text(m_logText, "roboto_mono", 12, textColor,
        TextAlign.TopLeft, Wrap.Wrap);
}
ScrollView.End(ui);

For the helper, refer to Add a scroll view.

Repeated list items

A vertical list with one button per item; each iteration scoped to a unique ID.

int rowHeight = 28;
ScrollView.Begin(ui, "items",
    new Vector2Int(contentW, items.Count * rowHeight),
    ScrollView.s_scrollConfigVertical);
{
    ui.PushSeriesVertical(rowHeight);
    {
        for (int i = 0; i < items.Count; i++)
        {
            ui.PushID(items[i].Id);

            ui.FillRounded(ui.IsHovered() ? hoverColor : restColor, 4);
            ui.Text(items[i].Name, "roboto_mono", 14, textColor,
                TextAlign.MiddleLeft);

            if (ui.Button("row"))
                Open(items[i]);

            ui.PopID();

            if (i < items.Count - 1) ui.Next();
        }
    }
    ui.Pop();
}
ScrollView.End(ui);

Animation

Hover colour transition

The button colour blends from rest to hover and back.

var ctx = ui.PushAnimate("save")
    .Init().Color().Set(restColor).Done()
    .Enter().Color().Blend(restColor, hoverColor, 0.2f).Done()
    .Leave().Color().Blend(hoverColor, restColor, 0.2f).Done()
    .BoolWhileTrue("hover", ui.IsHovered())
    .Apply();

ui.FillRounded(ctx.GetColor(), 5);
ui.Text("Save", "roboto_mono", 14, Color.white,
    TextAlign.MiddleCenter);
if (ui.Button("save")) Save();

ui.PopAnimate();

For the workflow, refer to Add an animation.

Press scale animation

The button shrinks while held, springs back on release.

var ctx = ui.PushAnimate("press")
    .Init().Scale().Set(1.0f).Done()
    .BoolBecomesTrue("down", ui.Button("press"))
        .Scale().Blend(1.0f, 0.9f, 0.08f).Done()
    .BoolBecomesFalse("down", ui.Button("press"))
        .Scale().Blend(0.9f, 1.0f, 0.15f).Done()
    .Apply();

ui.PushScale(ctx.GetFloat("scale"));
ui.FillRounded(buttonColor, 5);
ui.Text("Press me", "roboto_mono", 14, Color.white,
    TextAlign.MiddleCenter);
ui.Pop();

ui.PopAnimate();

Slide-in reveal

A panel that slides into view the first frame it's visible.

if (ui.BeginPanel("drawer", drawerRect))
{
    var ctx = ui.PushAnimate("drawer-slide")
        .Init().Float().Set(0.0f).Done()
        .Enter().Float().Blend(0.0f, 1.0f, 0.25f).Done()
        .BoolWhileTrue("show", true)
        .Apply();

    float t = ctx.GetFloat("offset");
    ui.Offset((1f - t) * drawerRect.width, 0);

    ui.FillRounded(drawerColor, 8);
    DrawDrawerBody(ui);

    ui.PopAnimate();
    ui.EndPanel();
}

Drawing

Card with drop shadow

A rounded card with a soft shadow under it.

using (ui.Auto().Center(320, 180))
{
    ui.DropShadow(new Vector2(0, 4), 12, 8,
        Color.black.WithAlpha(0.35f));
    ui.FillRounded(cardColor, 8);

    using (ui.Auto().Pad(16))
    {
        ui.Text("Card title", "roboto_mono", 18, textColor,
            TextAlign.TopLeft);
    }
}

For the primitive, refer to Drawing primitives.

Frosted-glass overlay

A translucent panel that blurs the content beneath it.

DrawBackgroundContent(ui);

using (ui.Auto().Center(400, 200))
{
    ui.FillFrostedRounded(blur: 12, new CornerRadius(8),
        Color.white.WithAlpha(0.05f));
    using (ui.Auto().Pad(16))
    {
        ui.Text("Frosted panel", "roboto_mono", 16, Color.white,
            TextAlign.TopLeft);
    }
}

Progress bar

A horizontal bar filled to a 0-1 fraction of the rect.

using (ui.Auto().Top(8))
{
    ui.FillRounded(trackColor, 4);
    using (ui.Auto().PushRelativeRect(
        new Rect(0, 0, ui.PeekWidth() * progress01, ui.PeekHeight())))
    {
        ui.FillRounded(fillColor, 4);
    }
}

Image with rounded corners

A texture clipped to a rounded rect.

using (ui.Auto().Center(96, 96))
{
    ui.ImageRounded(portraitTexture, new CornerRadius(8));
}

For the by-name lookup variant, refer to Drawing primitives.

Resources and theming

Push a theme overlay at runtime

A theme provider sits on top of the chain until you switch back.

m_baseGameUI.ResourceProviders.Push(m_darkThemeProvider);

// later, on theme change:
m_baseGameUI.ResourceProviders.Remove(m_darkThemeProvider);
m_baseGameUI.ResourceProviders.Push(m_lightThemeProvider);

For the model, refer to Resource providers.

Image by resource name

Look up a texture through the provider chain at draw time so theme swaps take effect without a code change.

using (ui.Auto().Center(48, 48))
{
    ui.ImageByName("icon-save", iconColor);
}

Additional resources