//! Demonstrates a simple, unstyled [`EditableText`] widget. //! //! [`EditableText`] is a basic primitive for text input in Bevy UI. //! In most cases, this should be combined with other entities to create a compound widget //! that includes e.g. a background, border, and text label. //! //! Note that while Bevy does offer clipboard support, access to the system clipboard is gated //! behind an off-by-default feature (`system_clipboard` on `bevy_clipboard`). //! When this is disabled, clipboard operations (copy, cut, paste) will operate on a simple in-memory buffer //! that is not shared with the operating system. //! This means that, unless you enable this feature, //! you will not be able to copy text from your application and paste it into another application, or vice versa. //! //! Most applications that use text input will want to enable system clipboard support to meet user expectations for copy/paste behavior. //! It is off by default to avoid forcing clipboard permissions on applications that do not need it but wish to use Bevy's UI solution for other widgets, //! and to avoid including the `arboard` dependency on platforms where it is not supported or where clipboard access is not desired. //! While desktop platforms generally support clipboard access without special permissions, some platforms (notably web and mobile) //! may require additional permissions or user gestures to allow clipboard access; //! this approach allows developers to opt in to full clipboard support only when they genuinely need it. //! //! To test this example using the system feature, run `cargo run --example text_input --features="system_clipboard"`. //! To enable this feature in your own project, add the `system_clipboard` feature to your list of enabled features for `bevy` in your `Cargo.toml`. //! //! See the module documentation for [`editable_text`](bevy::ui_widgets::editable_text) for more details. use bevy::color::palettes::css::DARK_GREY; use bevy::color::palettes::tailwind::SLATE_300; use bevy::input_focus::AutoFocus; use bevy::input_focus::{ tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, InputFocus, }; use bevy::prelude::*; use bevy::text::{EditableText, TextCursorStyle}; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugins(TabNavigationPlugin) .add_systems(Startup, setup) .add_systems(Update, text_submission) .run(); } #[derive(Component)] struct TextOutput; fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2d); let root = commands .spawn(Node { align_items: AlignItems::Center, flex_direction: FlexDirection::Column, padding: px(20).all(), row_gap: px(16), margin: auto().all(), ..default() }) .id(); let text_instructions = commands .spawn(( Text::new("Enter to submit text\nTab to switch inputs"), TextFont { font: asset_server.load("fonts/FiraSans-Bold.ttf").into(), font_size: FontSize::Px(25.0), ..default() }, )) .id(); let text_input_left = build_input_text(&mut commands, true, 24.0); let text_input_right = build_input_text(&mut commands, false, 24.0); let input_container = commands .spawn(( Node { column_gap: px(16), ..default() }, AutoFocus, TabGroup::new(0), )) .id(); // Set up a text output to see the result of our text input let text_output = commands .spawn(( Node { width: px(400), border: px(2).all(), padding: px(8).all(), ..Default::default() }, BorderColor::from(Color::from(SLATE_300)), Text::new(""), TextOutput, TextLayout { linebreak: LineBreak::WordOrCharacter, ..default() }, TextFont { font_size: FontSize::Px(24.0), ..default() }, )) .id(); commands .entity(input_container) .add_children(&[text_input_left, text_input_right]); commands .entity(root) .add_children(&[text_instructions, input_container, text_output]); } fn build_input_text(commands: &mut Commands, is_left: bool, font_size: f32) -> Entity { commands .spawn(( Node { border: px(2).all(), ..Default::default() }, BorderColor::from(Color::from(SLATE_300)), Name::new(if is_left { "Left" } else { "Right" }), EditableText { visible_width: Some(10.), allow_newlines: false, ..Default::default() }, TextLayout::no_wrap(), TextFont { font_size: FontSize::Px(font_size), ..default() }, TextCursorStyle::default(), TabIndex(if is_left { 0 } else { 1 }), BackgroundColor(DARK_GREY.into()), )) .id() } // Submit the text when Ctrl+Enter is pressed fn text_submission( input_focus: Res, keyboard_input: Res>, mut text_input: Query<(&mut EditableText, &Name)>, mut text_output: Single<&mut Text, With>, ) { if keyboard_input.just_pressed(KeyCode::Enter) && let Some(focused_entity) = input_focus.get() && let Ok((mut text_input, name)) = text_input.get_mut(focused_entity) { text_output.0 = format!("{:}: {:}", name, text_input.value()); text_input.clear(); } }