# 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,
}
```