---
# System prepended metadata

title: Bevy Gamepad Navigation
tags: [Input, UI]

---

# Bevy Gamepad Navigation

- Virtual cursors are terrible: lots of time wasted travelling between interactive elements, poor discoverability, bad for unconventional input devices (a11y)
- Tab cycling is... better, but still doesn't take advantage of full capabilities
- The natural choice is to navigate directly between elements based on quadrants (or octants, or directions)
- This creates a **focus graph**, made out of **focus paths**
    - Digraph time!
- A good focus graph would be:
    - Fully connected: no stranded UI elements
    - Reversible: reversing the input should go back to the previous element
    - Intuitive: paths map to the directions displayed on the screen
    - Forgiving: precise inputs are not needed to navigate to a specific element 
- We can probably do a decent job autogenerating these
- But devs require more customizability!
    - Reversibility is tricky when you have multiple segments to navigate between: storing a bit of history would be nice
    - Implementing cycling at the end of a list
    - Probably other cursed things
- Strategy:
    - Use octants for navigation path directions as a balance between good control and being forgiving
    - Automatically generate focus graph when things are spawned
    - Regenerate it when things change (ideally only focusing on local changes to save work)
    - Allow for custom behavior, overwriting any existing path
    - Store previous focus on InputFocus, and pass in `InputFocus` as context for custom behavior 
    - Don't overwrite custom behavior when regenerating
    - Return a Result from each navigation operation

PR strategy:

1. ~~Store previous focus~~
    - Not actually useful: this is widget logic. Was implemented at https://github.com/alice-i-cecile/bevy/tree/previous-focus
3. ~~Return a result from navigation operation~~
4. Manually constructed navigation graphs + manual navigation + example
5. Helper to automatically generate a navigation graph, building out from any existing manually constructed graph
6. Incremental + automatic regeneration of navigation graph

```rust
struct FocusGraph {
    edges: HashMap<Entity, FocusNode>
}

impl FocusGraph {
    fn navigate(direction: CompassOctant, input_focus: &InputFocus) -> Result<Entity, NavigationError>{
        todo!()
    }
}

struct FocusNode {
    // Probably actually an array for perf
    edges: HashMap<CompassOctant, FocusPath>
}

enum FocusPath {
    Generated(Entity),
    Manual(Entity),
    Contextual(Box<dyn Fn(InputFocus) -> Entity>),
    None,
}
```