-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Feathers text input widget #23645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Feathers text input widget #23645
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
e922391
Work on feathers text input
viridia 2b6fc89
Divided text input into parts; now sets hex value as you drag sliders.
viridia def60bb
Merge remote-tracking branch 'upstream/main' into feathers_text_input
viridia 913773f
merge.
viridia 9305fde
Change color of disabled text input
viridia 7694101
Merge remote-tracking branch 'upstream/main' into feathers_text_input
viridia ca4d51a
Unused import.
viridia 4410c7f
Focus outline for text input
viridia a7172a5
Merge remote-tracking branch 'upstream/main' into feathers_text_input
viridia 6ea50c1
Two way binding for feathers hex color input.
viridia 813cd20
Review feedback.
viridia 21516ff
Merge remote-tracking branch 'upstream/main' into feathers_text_input
viridia a7ab5c7
Added comment per request.
viridia e52ace5
Update crates/bevy_feathers/src/controls/text_input.rs
viridia File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,216 @@ | ||
| use bevy_app::{Plugin, PreUpdate}; | ||
| use bevy_ecs::{ | ||
| change_detection::{DetectChanges, DetectChangesMut}, | ||
| component::Component, | ||
| entity::Entity, | ||
| hierarchy::ChildOf, | ||
| lifecycle::RemovedComponents, | ||
| query::{Added, Has, With}, | ||
| schedule::IntoScheduleConfigs, | ||
| system::{Commands, Query, Res}, | ||
| }; | ||
| use bevy_input_focus::{tab_navigation::TabIndex, InputFocus, InputFocusVisible}; | ||
| use bevy_picking::PickingSystems; | ||
| use bevy_scene::prelude::*; | ||
| use bevy_text::{EditableText, FontSize, FontWeight, LineBreak, TextCursorStyle, TextLayout}; | ||
| use bevy_ui::{ | ||
| px, AlignItems, BorderColor, BorderRadius, Display, InteractionDisabled, JustifyContent, Node, | ||
| UiRect, Val, | ||
| }; | ||
|
|
||
| use crate::{ | ||
| constants::{fonts, size}, | ||
| cursor::EntityCursor, | ||
| font_styles::InheritableFont, | ||
| theme::{ThemeBackgroundColor, ThemeBorderColor, ThemeFontColor, ThemedText, UiTheme}, | ||
| tokens, | ||
| }; | ||
|
|
||
| /// Marker to indicate a text input widget with feathers styling. | ||
| #[derive(Component, Default, Clone)] | ||
| struct FeathersTextInputContainer; | ||
|
|
||
| /// Marker to indicate the inner part of the text input widget. | ||
| #[derive(Component, Default, Clone)] | ||
| struct FeathersTextInput; | ||
|
|
||
| /// Parameters for the text input template, passed to [`text_input`] function. | ||
| pub struct TextInputProps { | ||
| /// Max characters | ||
| pub max_characters: Option<usize>, | ||
| } | ||
|
|
||
| /// Decorative frame around a text input widget. This is a separate entity to allow icons | ||
| /// (such as "search" or "clear") to be inserted adjacent to the input. | ||
| pub fn text_input_container() -> impl Scene { | ||
| bsn! { | ||
| Node { | ||
| height: size::ROW_HEIGHT, | ||
| display: Display::Flex, | ||
| justify_content: JustifyContent::Center, | ||
| align_items: AlignItems::Center, | ||
| padding: UiRect::axes(Val::Px(4.0), Val::Px(0.)), | ||
| border: UiRect::all(Val::Px(2.0)), | ||
| flex_grow: 1.0, | ||
| border_radius: {BorderRadius::all(px(4.0))}, | ||
| column_gap: px(4), | ||
| } | ||
| FeathersTextInputContainer | ||
| ThemeBorderColor(tokens::TEXT_INPUT_BG) | ||
| ThemeBackgroundColor(tokens::TEXT_INPUT_BG) | ||
| ThemeFontColor(tokens::TEXT_INPUT_TEXT) | ||
| InheritableFont { | ||
| font: fonts::REGULAR, | ||
| font_size: FontSize::Px(13.0), | ||
| weight: FontWeight::NORMAL, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Scene function to spawn a text input. For proper styling, this should be enclosed by a | ||
| /// `text_input_container`. | ||
| /// | ||
| /// ```ignore | ||
| /// :text_input_container | ||
| /// Children [ | ||
| /// text_input(props) | ||
| /// ] | ||
| /// ``` | ||
| /// | ||
| /// # Arguments | ||
| /// * `props` - construction properties for the text input. | ||
| pub fn text_input(props: TextInputProps) -> impl Scene { | ||
| bsn! { | ||
| Node { | ||
| flex_grow: 1.0, | ||
| } | ||
| FeathersTextInput | ||
| EditableText { | ||
| cursor_width: 0.3, | ||
| max_characters: {props.max_characters}, | ||
| } | ||
| TextLayout { | ||
| linebreak: LineBreak::NoWrap, | ||
| } | ||
| TabIndex(0) | ||
| ThemedText | ||
| EntityCursor::System(bevy_window::SystemCursorIcon::Text) | ||
| TextCursorStyle::default() | ||
| } | ||
| } | ||
|
|
||
| fn update_text_cursor_color( | ||
| mut q_text_input: Query<&mut TextCursorStyle, With<FeathersTextInput>>, | ||
| theme: Res<UiTheme>, | ||
| ) { | ||
| if theme.is_changed() { | ||
| for mut cursor_style in q_text_input.iter_mut() { | ||
| cursor_style.color = theme.color(&tokens::TEXT_INPUT_CURSOR); | ||
| cursor_style.selection_color = theme.color(&tokens::TEXT_INPUT_SELECTION); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn update_text_input_styles( | ||
| q_inputs: Query< | ||
| (Entity, Has<InteractionDisabled>, &ThemeFontColor), | ||
| (With<FeathersTextInput>, Added<InteractionDisabled>), | ||
| >, | ||
| mut commands: Commands, | ||
| ) { | ||
| for (input_ent, disabled, font_color) in q_inputs.iter() { | ||
| set_text_input_styles(input_ent, disabled, font_color, &mut commands); | ||
| } | ||
| } | ||
|
|
||
| fn update_text_input_styles_remove( | ||
| q_inputs: Query<(Entity, Has<InteractionDisabled>, &ThemeFontColor), With<FeathersTextInput>>, | ||
| mut removed_disabled: RemovedComponents<InteractionDisabled>, | ||
| mut commands: Commands, | ||
| ) { | ||
| removed_disabled.read().for_each(|ent| { | ||
| if let Ok((input_ent, disabled, font_color)) = q_inputs.get(ent) { | ||
| set_text_input_styles(input_ent, disabled, font_color, &mut commands); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| fn update_text_input_focus( | ||
| q_inputs: Query<(), With<FeathersTextInput>>, | ||
| q_input_containers: Query<(Entity, &mut BorderColor), With<FeathersTextInputContainer>>, | ||
| parents: Query<&ChildOf>, | ||
| focus: Res<InputFocus>, | ||
| focus_visible: Res<InputFocusVisible>, | ||
| theme: Res<UiTheme>, | ||
| ) { | ||
| // We're not using FocusIndicator here because (a) the focus ring is inset rather than | ||
| // an outline, and (b) we want to detect focus on a descendant rather than an ancestor. | ||
| if focus.is_changed() { | ||
| let focus_parent = focus.0.and_then(|focus_ent| { | ||
| if focus_visible.0 && q_inputs.contains(focus_ent) { | ||
| parents | ||
| .iter_ancestors(focus_ent) | ||
| .find(|ent| q_input_containers.contains(*ent)) | ||
| } else { | ||
| None | ||
| } | ||
| }); | ||
|
|
||
| for (container, mut border_color) in q_input_containers { | ||
| let new_border_color = if Some(container) == focus_parent { | ||
| theme.color(&tokens::FOCUS_RING) | ||
| } else { | ||
| theme.color(&tokens::TEXT_INPUT_BG) | ||
| }; | ||
|
|
||
| border_color.set_if_neq(BorderColor::all(new_border_color)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn set_text_input_styles( | ||
| input_ent: Entity, | ||
| disabled: bool, | ||
| font_color: &ThemeFontColor, | ||
| commands: &mut Commands, | ||
| ) { | ||
| let font_color_token = match disabled { | ||
alice-i-cecile marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| true => tokens::TEXT_INPUT_TEXT_DISABLED, | ||
| false => tokens::TEXT_INPUT_TEXT, | ||
| }; | ||
|
|
||
| let cursor_shape = match disabled { | ||
| true => bevy_window::SystemCursorIcon::NotAllowed, | ||
| false => bevy_window::SystemCursorIcon::Text, | ||
| }; | ||
|
|
||
| // Change font color | ||
| if font_color.0 != font_color_token { | ||
| commands | ||
| .entity(input_ent) | ||
| .insert(ThemeFontColor(font_color_token)); | ||
| } | ||
|
|
||
| // Change cursor shape | ||
| commands | ||
| .entity(input_ent) | ||
| .insert(EntityCursor::System(cursor_shape)); | ||
| } | ||
|
|
||
| /// Plugin which registers the systems for updating the text input styles. | ||
| pub struct TextInputPlugin; | ||
|
|
||
| impl Plugin for TextInputPlugin { | ||
| fn build(&self, app: &mut bevy_app::App) { | ||
| app.add_systems( | ||
| PreUpdate, | ||
| ( | ||
| update_text_cursor_color, | ||
| update_text_input_styles, | ||
| update_text_input_styles_remove, | ||
| update_text_input_focus, | ||
| ) | ||
| .in_set(PickingSystems::Last), | ||
| ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we hook into the existing FocusIndicator infrastructure here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't, for two reasons:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kk, that's fine, but leave a comment to this effect please :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment added; I suppose it's possible to generalize
FocusIndicatorin some way, but it seems like it would be complicated. In CSS this would be handled by the:focus-withinpseudo-class.