Skip to content

Show a modal

A modal panel blocks input to everything below it. Use this pattern for confirmation dialogs, settings windows, and any flow that requires the player to complete or dismiss the interaction before returning to the underlying screen.

This page assumes you understand the panel system at a conceptual level. For the model, refer to Panels and modals.

To show a modal

  1. Define the panel. Use BeginPanel(id, rect, config) with a PanelConfig that has Hidden = true (the panel starts hidden) and a BackdropColor (Tick UI dims the rest of the screen before drawing the body). The panel blocks input only when you call ShowModal on it, as shown in step 3.
    if (ui.BeginPanel("confirm_quit", screenRect, new PanelConfig
    {
        Hidden = true,
        BackdropColor = new Color(0, 0, 0, 0.5f),
    }))
    {
        // body
        ui.EndPanel();
    }
    
  2. Render the dialog body. Centre a sized rect with ui.Auto().Center(width, height), draw a panel, and add the buttons. The body needs no ui.Fill for the dim background, because BackdropColor provides it. The body needs no ui.ConsumeInput(), because ShowModal in the next step blocks input underneath.
    using (ui.Auto().Center(360, 160))
    {
        ui.FillRounded(panelColor, 8);
        ui.Text("Quit without saving?", "roboto_mono", 18, Color.white,
            TextAlign.MiddleCenter);
    
        // ... buttons ...
    }
    
  3. Trigger the modal from outside the body. ui.ShowModal("confirm_quit") makes the panel visible and sets its BlocksInput flag. ui.ShowPanel(id) does the same visibility flip without the input block. ui.HidePanel(id) hides it again. Call these from your application logic, not from inside the panel body.
    if (m_userPressedQuit)
        ui.ShowModal("confirm_quit");
    

The full pattern, end to end

if (ui.BeginPanel("confirm_quit", screenRect, new PanelConfig
{
    Hidden = true,
    BackdropColor = new Color(0, 0, 0, 0.5f),
}))
{
    using (ui.Auto().Center(360, 160))
    {
        ui.FillRounded(panelColor, 8);
        ui.Text("Quit without saving?", "roboto_mono", 18, Color.white,
            TextAlign.MiddleCenter);

        using (ui.Auto().Bottom(40).PadHorizontal(20))
        using (ui.PushSplitHorizontal(2, 8))
        {
            using (ui.Auto())
            {
                ui.FillRounded(buttonColor, 5);
                ui.Text("Cancel", "roboto_mono", 14, Color.white,
                    TextAlign.MiddleCenter);
                if (ui.Button("cancel"))
                    ui.HidePanel("confirm_quit");
            }

            ui.Next();

            using (ui.Auto())
            {
                ui.FillRounded(dangerColor, 5);
                ui.Text("Quit", "roboto_mono", 14, Color.white,
                    TextAlign.MiddleCenter);
                if (ui.Button("quit"))
                    Application.Quit();
            }
        }
    }

    ui.EndPanel();
}

First-frame initialisation

To seed state when the modal opens (focus a textbox, reset a selection, run an entry animation), check IsPanelFirstShown inside the body:

if (ui.IsPanelFirstShown())
    ui.SetFocus("username");

IsPanelFirstShown returns true for the frame the panel was not used last frame. That covers the first appearance and any re-show after a hide. For the visibility state machine, refer to Panels and modals.

Animate the modal

Pair the modal with a PushAnimate scope to fade or scale the dialog body in. The Hiding visibility state gives the body one final frame before the panel transitions to Hidden, rather than being cut off the instant it is hidden.

For the procedure, refer to Add an animation. For the visibility state machine, refer to Panels and modals.

Pitfalls

Calling ShowModal inside the BeginPanel block. : Legal but confusing. Keep state changes outside the body.

The modal does not block input. : You called ShowPanel(id) instead of ShowModal(id). ShowPanel makes the panel visible. ShowModal makes it visible and sets BlocksInput, so the priority walk in BeginFrame prefers it over anything underneath.

There's no dim background. : The PanelConfig is missing BackdropColor. Set it to a translucent colour (e.g. new Color(0, 0, 0, 0.5f)). The backdrop draws automatically before the panel body runs.

The dialog never appears. : ShowModal is being called every frame and immediately cancelled by HidePanel. Audit the call sites. A single ShowModal outside a loop is correct.

Additional resources