Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
45a49c1
docs: adds example combining auto and manual directional nav
kfc35 Jan 4, 2026
6603d48
style: fmts and docs: update README with new example
kfc35 Jan 6, 2026
5df493d
refactor: renames and moves around navigation examples. edits some co…
kfc35 Jan 11, 2026
665f8b8
fix: update_focus_display only if changed
kfc35 Jan 11, 2026
c08a59f
docs: updates combined example to use pages of buttons
kfc35 Jan 12, 2026
04223e2
docs: adds finishing ui touches to example
kfc35 Jan 12, 2026
0d5c210
docs: updates comments
kfc35 Jan 12, 2026
c4f2d16
style: clippy and rename example
kfc35 Jan 12, 2026
425a63b
nit: use input_focus method
kfc35 Jan 12, 2026
b418a3b
docs: remove KeyDisplay component from next/prev page
kfc35 Jan 12, 2026
26fde3e
docs: nit rewording
kfc35 Jan 12, 2026
b25d7d6
docs: refine the comments more
kfc35 Jan 12, 2026
41f9ac6
style: fmt
kfc35 Jan 12, 2026
1c9946f
docs: each page shows a diff technique for manual overrides
kfc35 Jan 13, 2026
24b8cad
docs: update comment for setup fn
kfc35 Jan 13, 2026
bd41c18
docs: adds backticks where needed
kfc35 Jan 13, 2026
5c5ddd1
docs: button -> btn
kfc35 Jan 13, 2026
9ed9aa2
docs: currently on
kfc35 Jan 13, 2026
b769de6
style: add missing ; clippy
kfc35 Jan 13, 2026
d6a8a4c
refactor: renames examples to clarify intent, removes manual nav example
kfc35 Jan 13, 2026
f246aeb
docs: comment pass on dir nav classes
kfc35 Jan 13, 2026
093af72
docs: fix names within examples themselves
kfc35 Jan 13, 2026
fb84a03
docs: doc-link structs in examples
kfc35 Jan 14, 2026
fa81147
docs: clean up outdated comment
kfc35 Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5081,25 +5081,25 @@ wasm = true

[[example]]
name = "directional_navigation"
path = "examples/ui/directional_navigation.rs"
path = "examples/ui/navigation/directional_navigation.rs"
# Causes an ICE on docs.rs
doc-scrape-examples = false

[package.metadata.example.directional_navigation]
name = "Directional Navigation"
description = "Demonstration of Directional Navigation between UI elements"
description = "Demonstration of automatic directional navigation based on UI element positions"
category = "UI (User Interface)"
wasm = true

[[example]]
name = "auto_directional_navigation"
path = "examples/ui/auto_directional_navigation.rs"
name = "directional_navigation_overrides"
path = "examples/ui/navigation/directional_navigation_overrides.rs"
# Causes an ICE on docs.rs
doc-scrape-examples = false

[package.metadata.example.auto_directional_navigation]
name = "Automatic Directional Navigation"
description = "Demonstration of automatic directional navigation graph generation based on UI element positions"
[package.metadata.example.directional_navigation_overrides]
name = "Directional Navigation Overrides"
description = "Demonstration of automatic directional navigation between UI elements with manual overrides"
category = "UI (User Interface)"
wasm = true

Expand Down
51 changes: 33 additions & 18 deletions crates/bevy_input_focus/src/directional_navigation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
//! A navigation framework for moving between focusable elements based on directional input.
//! A manual navigation framework for moving between focusable elements based on directional input.
//!
//! Note: If using `bevy_ui`, this manual navigation framework is used to provide overrides
//! for its automatic navigation framework based on the `AutoDirectionalNavigation` component.
//! Most times, the automatic navigation framework alone should be sufficient.
//! If not using `bevy_ui`, this manual navigation framework can still be used by itself.
//!
//! While virtual cursors are a common way to navigate UIs with a gamepad (or arrow keys!),
//! they are generally both slow and frustrating to use.
Expand All @@ -7,15 +12,15 @@
//! Like the rest of this crate, the [`InputFocus`] resource is manipulated to track
//! the current focus.
//!
//! This module's [`DirectionalNavigationMap`] stores a directed graph of focusable entities.
//! Each entity can have up to 8 neighbors, one for each [`CompassOctant`], balancing
//! flexibility and required precision.
//!
//! Navigating between focusable entities (commonly UI nodes) is done by
//! passing a [`CompassOctant`] into the [`navigate`](DirectionalNavigation::navigate) method
//! from the [`DirectionalNavigation`] system parameter. Under the hood, an entity is found
//! automatically via brute force search in the desired [`CompassOctant`] direction.
//!
//! If some manual navigation is desired, a [`DirectionalNavigationMap`] will override the brute force
//! search in a direction for a given entity. The [`DirectionalNavigationMap`] stores a directed graph
//! of focusable entities. Each entity can have up to 8 neighbors, one for each [`CompassOctant`],
//! balancing flexibility and required precision.
//! from the [`DirectionalNavigation`] system parameter. Under the hood, the
//! [`DirectionalNavigationMap`] is used to return the focusable entity in a direction
//! for a given entity.
//!
//! # Setting up Directional Navigation
//!
Expand All @@ -26,21 +31,26 @@
//! include automatic navigation, you should also use the `AutoDirectionalNavigator` system parameter
//! in that crate instead of [`DirectionalNavigation`].
//!
//! ## Manual Navigation
//!
//! You can also manually define navigation connections using methods like
//! [`add_edge`](DirectionalNavigationMap::add_edge) and
//! [`add_looping_edges`](DirectionalNavigationMap::add_looping_edges).
//!
//! ## Combining Automatic and Manual
//! ## Combining Automatic Navigation with Manual Overrides
//!
//! Following manual edges always take precedence, allowing you to use
//! automatic navigation for most UI elements while overriding specific connections for
//! special cases like wrapping menus or cross-layer navigation.
//! special cases like wrapping menus or cross-layer navigation. If you need to override
//! automatic navigation behavior, use the [`DirectionalNavigationMap`] to define
//! overriding edges between UI entities.
//!
//! ## Manual Navigation Only
//!
//! Manually define your navigation using the [`DirectionalNavigationMap`], and use the
//! [`DirectionalNavigation`] system parameter to navigate between components.
//! You can define navigation connections using methods like
//! [`add_edge`](DirectionalNavigationMap::add_edge) and
//! [`add_looping_edges`](DirectionalNavigationMap::add_looping_edges).
//!
//! ## When to Use Manual Navigation
//! ## When to Use Manual Navigation or Manual Overrides
//!
//! While automatic navigation is recommended for most use cases, manual navigation provides:
//! While automatic navigation is recommended and satisfactory for most use cases,
//! using manual navigation only or integrating manual overrides to automatic navigation provide:
//!
//! - **Precise control**: Define exact navigation flow, including non-obvious connections like looping edges
//! - **Cross-layer navigation**: Connect elements across different UI layers or z-index levels
Expand Down Expand Up @@ -189,6 +199,11 @@ impl NavNeighbors {
///
/// This graph must be built and maintained manually, and the developer is responsible for ensuring that it meets the above criteria.
/// Notably, if the developer adds or removes the navigability of an entity, the developer should update the map as necessary.
///
/// If the automatic navigation system in `bevy_ui` is being used, this resource can be used to specify
/// manual navigation overrides. Any navigation edges specified in this map take precedence over automatic
/// navigation. For example, if navigation on one side of the window should wrap around to
/// the other side of the window, this navigation behavior can be specified using this map.
#[derive(Resource, Debug, Default, Clone, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
Expand Down
27 changes: 23 additions & 4 deletions crates/bevy_ui/src/auto_directional_navigation.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
//! An automatic directional navigation system, powered by the [`AutoDirectionalNavigation`] component.
//! An automatic directional navigation system, powered by the [`AutoDirectionalNavigation`]
//! component.
//!
//! [`AutoDirectionalNavigator`] expands on the manual directional navigation system
//! provided by the [`DirectionalNavigation`] system parameter from `bevy_input_focus`.
//! Unlike the navigation system provided by `bevy_input_focus`, the automatic directional
//! navigation system does not require specifying navigation edges. Just simply add the
//! [`AutoDirectionalNavigation`] component to your entities, and the system will automatically
//! calculate the navigation edges between entities based on screen position.
//!
//! [`AutoDirectionalNavigator`] replaces the manual directional navigation system
//! provided by the [`DirectionalNavigation`] system parameter from `bevy_input_focus`. The
//! [`AutoDirectionalNavigator`] will first navigate using manual override edges defined in the
//! [`DirectionalNavigationMap`](bevy_input_focus::directional_navigation::DirectionalNavigationMap).
//! If no manual overrides are defined, automatic navigation will occur between entities based on
//! screen position.
//!
//! If any resulting navigation behavior is undesired, [`AutoNavigationConfig`] can be tweaked or
//! manual overrides can be specified using the
//! [`DirectionalNavigationMap`](bevy_input_focus::directional_navigation::DirectionalNavigationMap).

use crate::{ComputedNode, ComputedUiTargetCamera, UiGlobalTransform};
use bevy_camera::visibility::InheritedVisibility;
Expand Down Expand Up @@ -72,7 +86,7 @@ use bevy_reflect::{prelude::*, Reflect};
/// automatic navigation as needed.
///
/// Manual edges defined via [`DirectionalNavigationMap`](bevy_input_focus::directional_navigation::DirectionalNavigationMap)
/// are completely independent and will continue to work regardless of this component.
/// will override any automatically calculated edges.
///
/// # Additional Requirements
///
Expand Down Expand Up @@ -237,6 +251,11 @@ impl<'w, 's> AutoDirectionalNavigator<'w, 's> {
}
}

/// Util used to get the resulting bounds of a UI entity after applying its rotation.
///
/// This is necessary to apply because navigation should only use the final screen position
/// of an entity in automatic navigation calculations. These bounds are used as the entity's size in
/// [`FocusableArea`].
fn get_rotated_bounds(size: Vec2, rotation: f32) -> Vec2 {
if rotation == 0.0 {
return size;
Expand Down
4 changes: 2 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -567,12 +567,12 @@ Example | Description
Example | Description
--- | ---
[Anchor Layout](../examples/ui/anchor_layout.rs) | Shows an 'anchor layout' style of ui layout
[Automatic Directional Navigation](../examples/ui/auto_directional_navigation.rs) | Demonstration of automatic directional navigation graph generation based on UI element positions
[Borders](../examples/ui/borders.rs) | Demonstrates how to create a node with a border
[Box Shadow](../examples/ui/box_shadow.rs) | Demonstrates how to create a node with a shadow
[Button](../examples/ui/button.rs) | Illustrates creating and updating a button
[CSS Grid](../examples/ui/grid.rs) | An example for CSS Grid layout
[Directional Navigation](../examples/ui/directional_navigation.rs) | Demonstration of Directional Navigation between UI elements
[Directional Navigation](../examples/ui/navigation/directional_navigation.rs) | Demonstration of automatic directional navigation based on UI element positions
[Directional Navigation Overrides](../examples/ui/navigation/directional_navigation_overrides.rs) | Demonstration of automatic directional navigation between UI elements with manual overrides
[Display and Visibility](../examples/ui/display_and_visibility.rs) | Demonstrates how Display and Visibility work in the UI.
[Drag to Scroll](../examples/ui/drag_to_scroll.rs) | This example tests scale factor, dragging and scrolling
[Feathers Widgets](../examples/ui/feathers.rs) | Gallery of Feathers Widgets
Expand Down
Loading