---
title: "Controller and keyboard navigation"
url: https://recursiveai.org/onegame/cookbook/controller-navigation/
description: "Make a game screen playable with a gamepad and a keyboard: a default focus, d-pad movement, confirm and back, focus that stays inside a dialog, and…"
version: "1.2.0"
---

# Controller and keyboard navigation

Make a game screen playable with a gamepad and a keyboard: a default focus, d-pad movement, confirm and back, focus that stays inside a dialog, and scenarios that play the screen with a controller.

The UI owns one focused element. The host turns the gamepad into UI input by itself: South is confirm, East is back, and the d-pad and shoulder buttons navigate. Tab and Shift+Tab move through focusable elements in order, the arrow keys and the d-pad move to the nearest one in that direction, and Enter, Space and confirm click the focused element. Every kit component works this way, so a screen built from the kit needs only a starting focus.

This uses the market stall from [Build a game screen with the UI kit](https://recursiveai.org/onegame/cookbook/game-ui-screen/).

## Give the screen a starting focus

Without one, a controller player’s first press has to find something. The stall starts in the table:

```csharp
            // A controller player starts in the table. RequestFocus applies after the next update, once the screen is in the tree.
            game.Ui.RequestFocus(table.Body);
```

`RequestFocus` waits for the end of the next update, which is what code inside a screen builder or a binding must use. `ui.Focus(element)` focuses at once, and `FocusHelpers.FocusDefault(game.Ui, screen)` focuses the element marked `.AsDefaultFocus()`.

## Play it with a controller

```yaml
name: buy-with-pad
description: A controller player starts in the table, moves to pears and buys two with the confirm button.
steps:
  - step: 1
  - expect:
      ui: { id: goods/body, focused: true }
  - nav: down              # from apples to pears
  - nav: activate          # the confirm button: onActivate buys the selected row
  - step: 1
  - nav: activate
  - step: 1
expect:
  - state: { entity: Shop, property: "Stock[1]", equals: 6 }
  - state: { entity: Shop, property: Gold, equals: 42 }
```

```shell
onegame test --scenario scenarios/buy-with-pad.yaml
```

```text
PASS buy-with-pad (33 ms, tick 3, hash f8fcbc6f2c91948b)
1 passed, 0 failed
```

`nav:` takes `up down left right next previous activate cancel`, or a button by name (`start`, `back`, `west`, `north`, `left_stick`...). Pears cost 4, so two of them take the gold from 50 to 42 and the stock from 8 to 6. In the table, Up and Down move the selection and confirm calls the table’s `onActivate`. At the first or last row the key isn’t used, so focus moves on to the neighbor: that is how a pad player leaves a table.

Without the starting focus, the shoulder button (`nav: next`) walks the focusable elements in order. In this screen the first three are the table’s header cells (`goods/header/0` to `goods/header/2`) and the fourth is `goods/body`.

## Dialogs keep focus inside

The sell-back confirm dialog traps focus: while it is open, Tab and the d-pad stay inside it, the pointer can’t reach what is behind it, and Escape or the back button closes it and returns focus to what had it before. A dialog made with `danger: true` starts on its cancel button, so a stray confirm press doesn’t destroy anything. The [sell-back scenario](https://recursiveai.org/onegame/cookbook/game-ui-screen/) checks this with `press: Escape`, then `press: Tab` and `press: Enter`.

## Good to know

- A component that uses a key itself takes it first: a slider takes Left and Right, a text field the arrows. Unused keys fall back to moving focus.
- Disabled and hidden elements are skipped, and focusing an element scrolls a scroll view to show it.
- The UI guide’s rule of thumb: every action reachable by d-pad and confirm, back always leaves, and a default focus on every screen.
- Input glyphs (“press (A)”) follow the device in use. `KitHost.Dispatch` tells them which one that is.
- Steam Input action sets can be scripted in scenarios with `steam-input:` against a stand-in controller.

Focus, keys and the full input table are in [the UI guide](https://recursiveai.org/onegame/docs/ui-guide/), section 9.
