# UiScreenOptions

> Talos 0.7 · Since 0.6

Screen-level options, carried in the root node's `props`.

## Signature

```ts
export interface UiScreenOptions extends Record<string, unknown> {
  /** The input context the screen asks the game to be in while it is up. */
  inputContext?: string
  /** Block the pointer from what is under it, even where it hits nothing. */
  modal?: boolean
  /** Whether Escape and the pad's B pop it. Default true. */
  dismissible?: boolean
  /** Answer no hit-test and take no focus unless a node opts in. What the HUD is. */
  clickThrough?: boolean
  /** Whether the screen wants a visible cursor. Default true. */
  cursor?: boolean
  /**
   * Stop the world while the screen is on the stack: physics, behaviours' `update`, animation
   * and `ctx.time` hold, while the UI, `onEvent` and `pausedUpdate` run on. Default false.
   */
  pausesWorld?: boolean
}
```

Import: `import type { UiScreenOptions } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `inputContext` | `inputContext?: string` | The input context the screen asks the game to be in while it is up. |
| `modal` | `modal?: boolean` | Block the pointer from what is under it, even where it hits nothing. |
| `dismissible` | `dismissible?: boolean` | Whether Escape and the pad's B pop it. Default true. |
| `clickThrough` | `clickThrough?: boolean` | Answer no hit-test and take no focus unless a node opts in. What the HUD is. |
| `cursor` | `cursor?: boolean` | Whether the screen wants a visible cursor. Default true. |
| `pausesWorld` | `pausesWorld?: boolean` | Stop the world while the screen is on the stack: physics, behaviours' `update`, animation and `ctx.time` hold, while the UI, `onEvent` and `pausedUpdate` run on. Default false. |

## Example

The tested fixture `fixtures/recipes/pause-screen/scenes/behaviors/pause-screen.ts`.

```ts
import type { Behavior, Ctx } from "@argus/api"

/**
 * A pause key that toggles a modal pause screen, with the world held under it.
 *
 * P, Escape or Start on a pad opens it; P, Escape or its Resume button closes it.
 *
 * The screen is declared `pausesWorld`, so while it is on the stack the engine runs no physics, no
 * behaviour's `update` and no animation, and `ctx.time.now` stands still. Nothing here has to ask
 * whether it is paused. What still runs is the UI, the Resume button's `ui-activated` (into the
 * listener below) and `pausedUpdate`, which is where the key that closes the menu is read — `update`
 * is not running to read it.
 */
interface State {
  /** Seconds of play: `ctx.time.now`, which stops while the world is held. */
  clock: number
  paused: boolean
}

interface Props {
  /** The entity this game spins, to show the freeze. Default `spinner`. */
  spinner?: string
}

const PAUSE = "pause"

const isPaused = (ctx: Ctx<State, Props>): boolean => ctx.ui.stack().includes(PAUSE)

export default {
  init(ctx) {
    if (typeof ctx.state.clock !== "number") ctx.state.clock = 0
    ctx.state.paused = isPaused(ctx)
    ctx.events.on("ui-activated", (event) => {
      if (event.data.screen === PAUSE && event.data.id === "resume" && isPaused(ctx)) ctx.ui.pop()
    })
  },

  update(ctx) {
    // `pause` is bound in both contexts: `gameplay` to open the screen, and `menu` — the context
    // the screen switches to — to close it again with the same key, in `pausedUpdate`.
    if (ctx.input !== null && ctx.input.pressed("pause") && !isPaused(ctx)) ctx.ui.push(PAUSE)
    ctx.state.paused = false
    ctx.state.clock = ctx.time.now
    const spinner = ctx.world.find(ctx.props.spinner ?? "spinner")
    if (spinner) {
      const half = ctx.state.clock / 2
      spinner.rotation = [0, Math.sin(half), 0, Math.cos(half)]
    }
  },

  pausedUpdate(ctx) {
    ctx.state.paused = true
    if (ctx.input !== null && ctx.input.pressed("pause") && isPaused(ctx)) ctx.ui.pop()
  },
} satisfies Behavior<State, Props>
```
