Path: blob/main/examples/ui/text/multiline_text_input.rs
30635 views
//! Demonstrates a single, minimal multiline [`EditableText`] widget.12use bevy::color::palettes::css::DARK_SLATE_GRAY;3use bevy::color::palettes::tailwind::SLATE_300;4use bevy::input::keyboard::{Key, KeyboardInput};5use bevy::input_focus::tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin};6use bevy::input_focus::{AutoFocus, FocusedInput};7use bevy::prelude::*;8use bevy::text::{EditableText, EditableTextFilter, TextCursorStyle};9use bevy::ui_widgets::SelectAllOnFocus;1011fn main() {12App::new()13.add_plugins((DefaultPlugins, TabNavigationPlugin))14.add_systems(Startup, setup)15.run();16}1718#[derive(Component)]19struct MultilineInput;2021#[derive(Component)]22struct VisibleLinesInput;2324#[derive(Component)]25struct FontSizeInput;2627#[derive(Component)]28struct SelectionRadiusInput;2930fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {31commands.spawn(Camera2d);3233commands34.spawn(Node {35width: percent(100.),36height: percent(100.),37justify_content: JustifyContent::Center,38align_items: AlignItems::Center,39..default()40})41.with_children(|parent| {42parent43.spawn((44Node {45flex_direction: FlexDirection::Column,46align_items: AlignItems::End,47row_gap: px(10.),48..default()49},50TabGroup::default(),51))52.with_children(|parent| {53parent54.spawn((55Node {56width: px(450.),57border: px(2.).all(),58padding: px(8.).all(),59..default()60},61EditableText {62visible_lines: Some(8.),63allow_newlines: true,64..default()65},66TextLayout {67linebreak: LineBreak::WordOrCharacter,68..default()69},70TextCursorStyle {71color: Color::WHITE,72selected_text_color: Some(Color::BLACK),73..default()74},75TextFont {76font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),77font_size: FontSize::Px(30.),78..default()79},80BackgroundColor(DARK_SLATE_GRAY.into()),81BorderColor::all(SLATE_300),82MultilineInput,83TabIndex(0),84AutoFocus,85))86.observe(87|on: On<FocusedInput<KeyboardInput>>,88keys: Res<ButtonInput<Key>>,89input_query: Query<&EditableText, With<MultilineInput>>| {90if !(on.input.state.is_pressed()91&& on.input.logical_key == Key::Enter92&& keys.pressed(Key::Control))93{94return;95}96let Ok(input) = input_query.get(on.focused_entity) else {97return;98};99100let mut output = String::new();101output.reserve(input.value().into_iter().map(str::len).sum());102for sub_str in input.value() {103output.push_str(sub_str);104}105106info!("{output}" );107},108);109110parent111.spawn((112Node {113flex_direction: FlexDirection::Row,114column_gap: px(10.),115..default()116},117children![118(119Text::new("visible lines:"),120TextFont {121font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),122font_size: FontSize::Px(30.),123..default()124},125),126(127Node {128width: px(100.),129border: px(2.).all(),130..default()131},132TextFont {133font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),134font_size: FontSize::Px(30.),135..default()136},137TextLayout {138justify: Justify::End,139..default()140},141BackgroundColor(DARK_SLATE_GRAY.into()),142BorderColor::all(SLATE_300),143EditableText::new("8"),144EditableTextFilter::new(|c| c.is_ascii_digit() || c == '.'),145TextCursorStyle {146color: Color::WHITE,147selected_text_color: Some(Color::BLACK),148unfocused_selection_color: Color::NONE,149..default()150},151SelectAllOnFocus,152VisibleLinesInput,153TabIndex(1),154)155],156))157.observe(158|on: On<FocusedInput<KeyboardInput>>,159mut query_set: ParamSet<(160Query<&EditableText, With<VisibleLinesInput>>,161Query<&mut EditableText, With<MultilineInput>>,162)>| {163if !(on.input.state.is_pressed()164&& on.input.logical_key == Key::Enter)165{166return;167}168169let visible_lines_query = query_set.p0();170let Ok(input) = visible_lines_query.get(on.original_event_target())171else {172return;173};174175let mut output = String::new();176output.reserve(input.value().into_iter().map(str::len).sum());177for sub_str in input.value() {178output.push_str(sub_str);179}180181let Ok(lines) = output.parse::<f32>() else {182return;183};184185let mut multiline_query = query_set.p1();186let Ok(mut multiline_input) = multiline_query.single_mut() else {187return;188};189190multiline_input.visible_lines = Some(lines.clamp(1., 10.));191},192);193194parent195.spawn((196Node {197flex_direction: FlexDirection::Row,198column_gap: px(10.),199..default()200},201children![202(203Text::new("font size:"),204TextFont {205font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),206font_size: FontSize::Px(30.),207..default()208},209),210(211Node {212width: px(100.),213border: px(2.).all(),214..default()215},216TextFont {217font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),218font_size: FontSize::Px(30.),219..default()220},221TextLayout {222justify: Justify::End,223..default()224},225BackgroundColor(DARK_SLATE_GRAY.into()),226BorderColor::all(SLATE_300),227EditableText::new("30"),228EditableTextFilter::new(|c| c.is_ascii_digit()),229TextCursorStyle {230color: Color::WHITE,231selected_text_color: Some(Color::BLACK),232unfocused_selection_color: Color::NONE,233..default()234},235SelectAllOnFocus,236FontSizeInput,237TabIndex(2),238)239],240))241.observe(242|on: On<FocusedInput<KeyboardInput>>,243font_size_input_query: Query<&EditableText, With<FontSizeInput>>,244mut multiline_input_font: Single<245&mut TextFont,246With<MultilineInput>,247>| {248if !(on.input.state.is_pressed()249&& on.input.logical_key == Key::Enter)250{251return;252}253254let Ok(input) =255font_size_input_query.get(on.original_event_target())256else {257return;258};259260let mut output = String::new();261output.reserve(input.value().into_iter().map(str::len).sum());262for sub_str in input.value() {263output.push_str(sub_str);264}265266let Ok(font_size) = output.parse::<f32>() else {267return;268};269270multiline_input_font.font_size =271FontSize::Px(font_size.clamp(5., 50.));272},273);274275parent276.spawn((277Node {278flex_direction: FlexDirection::Row,279column_gap: px(10.),280..default()281},282children![283(284Text::new("corner radius:"),285TextFont {286font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),287font_size: FontSize::Px(30.),288..default()289},290),291(292Node {293width: px(100.),294border: px(2.).all(),295..default()296},297TextFont {298font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),299font_size: FontSize::Px(30.),300..default()301},302TextLayout {303justify: Justify::End,304..default()305},306BackgroundColor(DARK_SLATE_GRAY.into()),307BorderColor::all(SLATE_300),308EditableText::new("0"),309EditableTextFilter::new(|c| c.is_ascii_digit() || c == '.'),310TextCursorStyle {311color: Color::WHITE,312selected_text_color: Some(Color::BLACK),313..default()314},315SelectionRadiusInput,316TabIndex(2),317)318],319))320.observe(321|on: On<FocusedInput<KeyboardInput>>,322radius_input_query: Query<323&EditableText,324With<SelectionRadiusInput>,325>,326mut cursor_style: Single<327&mut TextCursorStyle,328With<MultilineInput>,329>| {330if !(on.input.state.is_pressed()331&& on.input.logical_key == Key::Enter)332{333return;334}335336let Ok(input) = radius_input_query.get(on.original_event_target())337else {338return;339};340341let mut output = String::new();342output.reserve(input.value().into_iter().map(str::len).sum());343for sub_str in input.value() {344output.push_str(sub_str);345}346347let Ok(radius) = output.parse::<f32>() else {348return;349};350351cursor_style.selection_radius = radius.clamp(0., 0.5);352},353);354});355});356}357358359