Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_feathers/src/controls/text_input.rs
30636 views
1
use bevy_app::{Plugin, PreUpdate, PropagateOver};
2
use bevy_asset::AssetServer;
3
use bevy_ecs::{
4
change_detection::DetectChanges,
5
entity::Entity,
6
lifecycle::RemovedComponents,
7
query::{Added, Has, With},
8
schedule::IntoScheduleConfigs,
9
system::{Commands, Query, Res},
10
template::template,
11
};
12
use bevy_input_focus::tab_navigation::TabIndex;
13
use bevy_picking::PickingSystems;
14
use bevy_scene::prelude::*;
15
use bevy_text::{
16
EditableText, FontSource, FontWeight, LineBreak, TextCursorStyle, TextFont, TextLayout,
17
};
18
use bevy_ui::{
19
px, AlignItems, BorderRadius, Display, InteractionDisabled, JustifyContent, Node, UiRect,
20
};
21
22
use crate::{
23
constants::{fonts, size},
24
cursor::EntityCursor,
25
focus::FocusWithinIndicator,
26
font_styles::InheritableFont,
27
theme::{InheritableThemeTextColor, ThemeBackgroundColor, UiTheme},
28
tokens,
29
};
30
31
/// Decorative frame around a text input widget. This is a separate entity to allow icons
32
/// (such as "search" or "clear") to be inserted adjacent to the input.
33
///
34
/// This is spawnable by inheriting it as a "scene component".
35
#[derive(SceneComponent, Default, Clone)]
36
pub struct FeathersTextInputContainer;
37
38
impl FeathersTextInputContainer {
39
fn scene() -> impl Scene {
40
bsn! {
41
Node {
42
height: size::ROW_HEIGHT,
43
display: Display::Flex,
44
justify_content: JustifyContent::Center,
45
align_items: AlignItems::Center,
46
padding: UiRect {
47
right: px(3.0),
48
},
49
border: UiRect {
50
left: px(3.0)
51
},
52
flex_grow: 1.0,
53
border_radius: {BorderRadius::all(px(4.0))},
54
column_gap: px(4),
55
}
56
FeathersTextInputContainer
57
FocusWithinIndicator
58
ThemeBackgroundColor(tokens::TEXT_INPUT_BG)
59
InheritableThemeTextColor(tokens::TEXT_INPUT_TEXT)
60
InheritableFont {
61
font: fonts::REGULAR,
62
font_size: size::COMPACT_FONT,
63
weight: FontWeight::NORMAL,
64
}
65
}
66
}
67
}
68
69
/// Scene function to spawn a text input. For proper styling, this should be enclosed by a [`FeathersTextInputContainer`].
70
///
71
/// This is spawnable by inheriting it as a "scene component" with optional [`FeathersTextInputProps`].
72
///
73
/// ```ignore
74
/// :FeathersTextInputContainer
75
/// Children [
76
/// :FeathersTextInput
77
/// ]
78
/// ```
79
#[derive(SceneComponent, Default, Clone)]
80
#[scene(FeathersTextInputProps)]
81
pub struct FeathersTextInput;
82
83
/// Props used to construct the [`FeathersTextInput`] scene.
84
#[derive(Default, Clone)]
85
pub struct FeathersTextInputProps {
86
/// Visible width
87
pub visible_width: Option<f32>,
88
/// Max characters
89
pub max_characters: Option<usize>,
90
}
91
92
impl FeathersTextInput {
93
fn scene(props: FeathersTextInputProps) -> impl Scene {
94
bsn! {
95
Node {
96
flex_grow: {
97
if props.visible_width.is_some() {
98
0.
99
} else {
100
1.
101
}
102
} ,
103
}
104
FeathersTextInput
105
EditableText {
106
cursor_width: 0.3,
107
visible_width: {props.visible_width},
108
max_characters: {props.max_characters},
109
}
110
TextLayout {
111
linebreak: LineBreak::NoWrap,
112
}
113
TabIndex(0)
114
template(|ctx| {
115
Ok(TextFont {
116
font: FontSource::Handle(ctx.resource::<AssetServer>().load(fonts::REGULAR)),
117
font_size: size::COMPACT_FONT,
118
weight: FontWeight::NORMAL,
119
..Default::default()
120
})
121
})
122
PropagateOver<TextFont>
123
EntityCursor::System(bevy_window::SystemCursorIcon::Text)
124
TextCursorStyle::default()
125
}
126
}
127
}
128
129
fn update_text_cursor_color(
130
mut q_text_input: Query<&mut TextCursorStyle, With<FeathersTextInput>>,
131
theme: Res<UiTheme>,
132
) {
133
if theme.is_changed() {
134
for mut cursor_style in q_text_input.iter_mut() {
135
cursor_style.color = theme.color(&tokens::TEXT_INPUT_CURSOR);
136
cursor_style.selection_color = theme.color(&tokens::TEXT_INPUT_SELECTION);
137
cursor_style.unfocused_selection_color =
138
theme.color(&tokens::TEXT_INPUT_SELECTION_UNFOCUSED);
139
}
140
}
141
}
142
143
fn update_text_input_styles(
144
q_inputs: Query<
145
(Entity, Has<InteractionDisabled>, &InheritableThemeTextColor),
146
(With<FeathersTextInput>, Added<InteractionDisabled>),
147
>,
148
mut commands: Commands,
149
) {
150
for (input_ent, disabled, font_color) in q_inputs.iter() {
151
set_text_input_styles(input_ent, disabled, font_color, &mut commands);
152
}
153
}
154
155
fn update_text_input_styles_remove(
156
q_inputs: Query<
157
(Entity, Has<InteractionDisabled>, &InheritableThemeTextColor),
158
With<FeathersTextInput>,
159
>,
160
mut removed_disabled: RemovedComponents<InteractionDisabled>,
161
mut commands: Commands,
162
) {
163
removed_disabled.read().for_each(|ent| {
164
if let Ok((input_ent, disabled, font_color)) = q_inputs.get(ent) {
165
set_text_input_styles(input_ent, disabled, font_color, &mut commands);
166
}
167
});
168
}
169
170
fn set_text_input_styles(
171
input_ent: Entity,
172
disabled: bool,
173
font_color: &InheritableThemeTextColor,
174
commands: &mut Commands,
175
) {
176
let font_color_token = match disabled {
177
true => tokens::TEXT_INPUT_TEXT_DISABLED,
178
false => tokens::TEXT_INPUT_TEXT,
179
};
180
181
let cursor_shape = match disabled {
182
true => bevy_window::SystemCursorIcon::NotAllowed,
183
false => bevy_window::SystemCursorIcon::Text,
184
};
185
186
// Change font color
187
if font_color.0 != font_color_token {
188
commands
189
.entity(input_ent)
190
.insert(InheritableThemeTextColor(font_color_token));
191
}
192
193
// Change cursor shape
194
commands
195
.entity(input_ent)
196
.insert(EntityCursor::System(cursor_shape));
197
}
198
199
/// Plugin which registers the systems for updating the text input styles.
200
pub struct TextInputPlugin;
201
202
impl Plugin for TextInputPlugin {
203
fn build(&self, app: &mut bevy_app::App) {
204
app.add_systems(
205
PreUpdate,
206
(
207
update_text_cursor_color,
208
update_text_input_styles,
209
update_text_input_styles_remove,
210
)
211
.in_set(PickingSystems::Last),
212
);
213
}
214
}
215
216