Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_feathers/src/focus.rs
30635 views
1
//! This module contains the infrastructure needed for displaying focus outlines.
2
use bevy_app::{Plugin, PostUpdate};
3
use bevy_ecs::{
4
change_detection::DetectChanges,
5
component::Component,
6
entity::Entity,
7
hierarchy::{ChildOf, Children},
8
query::With,
9
reflect::ReflectComponent,
10
schedule::IntoScheduleConfigs,
11
system::{Commands, Query, Res},
12
};
13
use bevy_input_focus::{InputFocus, InputFocusVisible};
14
use bevy_platform::collections::HashSet;
15
use bevy_reflect::{prelude::ReflectDefault, Reflect};
16
use bevy_ui::{px, Outline, UiSystems};
17
18
use crate::{theme::UiTheme, tokens};
19
20
/// A marker component which indicates that this entity should display a visible focus outline
21
/// when either it, or its ancestor, are focused. Insert this into a widget on the entity that
22
/// you wish to display a focus outline.
23
#[derive(Component, Default, Clone, Reflect)]
24
#[reflect(Component, Clone, Default)]
25
pub struct FocusIndicator;
26
27
/// A marker component which indicates that this entity should display a visible focus outline
28
/// when either it, or any descendant, are focused. Insert this into a widget on the entity that
29
/// you wish to display a focus outline.
30
#[derive(Component, Default, Clone, Reflect)]
31
#[reflect(Component, Clone, Default)]
32
pub struct FocusWithinIndicator;
33
34
fn manage_focus_indicators(
35
mut commands: Commands,
36
input_focus: Res<InputFocus>,
37
input_focus_visible: Res<InputFocusVisible>,
38
q_indicators: Query<Entity, With<FocusIndicator>>,
39
q_within_indicators: Query<Entity, With<FocusWithinIndicator>>,
40
q_children: Query<&Children>,
41
q_parents: Query<&ChildOf>,
42
theme: Res<UiTheme>,
43
) {
44
if !input_focus.is_changed() && !input_focus_visible.is_changed() && !theme.is_changed() {
45
return;
46
}
47
48
let mut visited = HashSet::<Entity>::with_capacity(q_indicators.count());
49
if let Some(focus) = input_focus.get()
50
&& input_focus_visible.0
51
{
52
// Look for focus in descendants
53
for entity in q_children
54
.iter_descendants(focus)
55
.chain(core::iter::once(focus))
56
{
57
if q_indicators.contains(entity) {
58
commands.entity(entity).insert(Outline {
59
color: theme.color(&tokens::FOCUS_RING),
60
width: px(2),
61
offset: px(2),
62
});
63
visited.insert(entity);
64
}
65
}
66
67
// Look for focus in ancestors
68
for entity in q_parents
69
.iter_ancestors(focus)
70
.chain(core::iter::once(focus))
71
{
72
if q_within_indicators.contains(entity) {
73
commands.entity(entity).insert(Outline {
74
color: theme.color(&tokens::FOCUS_RING),
75
width: px(2),
76
offset: px(2),
77
});
78
visited.insert(entity);
79
}
80
}
81
}
82
83
for entity in q_indicators.iter().chain(q_within_indicators.iter()) {
84
if !visited.contains(&entity) {
85
commands.entity(entity).remove::<Outline>();
86
}
87
}
88
}
89
90
/// Plugin which registers the systems for updating focus outlines.
91
pub struct FocusOutlinesPlugin;
92
93
impl Plugin for FocusOutlinesPlugin {
94
fn build(&self, app: &mut bevy_app::App) {
95
app.add_systems(
96
PostUpdate,
97
manage_focus_indicators.in_set(UiSystems::Content),
98
);
99
}
100
}
101
102