Skip to main content

virtual_keyboard/
virtual_keyboard.rs

1//! Virtual keyboard example
2
3use bevy::{
4    color::palettes::css::{NAVY, WHITE},
5    feathers::{
6        controls::{VirtualKeyPressed, VirtualKeyboard},
7        dark_theme::create_dark_theme,
8        theme::UiTheme,
9        FeathersPlugins,
10    },
11    input_focus::{
12        tab_navigation::{TabGroup, TabIndex},
13        AutoFocus, InputFocus,
14    },
15    prelude::*,
16    text::{EditableText, TextCursorStyle, TextEdit},
17    ui_widgets::TextInput,
18};
19
20fn main() {
21    App::new()
22        .add_plugins((DefaultPlugins, FeathersPlugins))
23        .insert_resource(UiTheme(create_dark_theme()))
24        .add_systems(Startup, scene.spawn())
25        .run();
26}
27
28fn on_virtual_key_pressed(
29    virtual_key_pressed: On<VirtualKeyPressed<&'static str>>,
30    mut query: Query<(Entity, &mut EditableText)>,
31    mut input_focus: ResMut<InputFocus>,
32) {
33    println!("Virtual keyboard key pressed: {}", virtual_key_pressed.key);
34    let Ok((entity_id, mut text)) = query.single_mut() else {
35        return;
36    };
37    text.queue_edit(match virtual_key_pressed.key {
38        "space" => TextEdit::Insert(" ".into()),
39        "enter" => TextEdit::Insert("\n".into()),
40        "backspace" => TextEdit::Backspace,
41        "left" => TextEdit::Left(false),
42        "right" => TextEdit::Right(false),
43        "up" => TextEdit::Up(false),
44        "down" => TextEdit::Down(false),
45        "home" => TextEdit::LineStart(false),
46        "end" => TextEdit::LineEnd(false),
47        key if key.len() == 1 => TextEdit::Insert(key.into()),
48        _ => return,
49    });
50    input_focus.set(entity_id, bevy::input_focus::FocusCause::Navigated);
51}
52
53fn scene() -> impl SceneList {
54    bsn_list! {
55        Camera2d
56        --
57        @text_input()
58        --
59        @keyboard()
60    }
61}
62
63fn keyboard() -> impl Scene {
64    let keys = [
65        vec!["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", ","],
66        vec!["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
67        vec!["A", "S", "D", "F", "G", "H", "J", "K", "L", "'"],
68        vec!["Z", "X", "C", "V", "B", "N", "M", "-", "/"],
69        vec!["space", "enter", "backspace"],
70        vec!["left", "right", "up", "down", "home", "end"],
71    ];
72
73    bsn! {
74        Node {
75            width: percent(100),
76            bottom: px(0),
77            flex_direction: FlexDirection::Column,
78            position_type: PositionType::Absolute,
79            align_items: AlignItems::Center,
80            justify_content: JustifyContent::End,
81        }
82        Children [
83            Node {
84                flex_direction: FlexDirection::Column,
85                border: px(5),
86                row_gap: px(5),
87                padding: px(5),
88                align_items: AlignItems::Center,
89                margin: px(25),
90                border_radius: BorderRadius::all(px(10)),
91            }
92            BackgroundColor(NAVY)
93            BorderColor::all(Color::WHITE)
94            Children [
95                Text("virtual keyboard")
96                --
97                @VirtualKeyboard::<&str> { @keys: keys }
98                on(on_virtual_key_pressed)
99            ]
100        ]
101    }
102}
103
104fn text_input() -> impl Scene {
105    bsn! {
106        Node {
107            width: percent(100),
108            padding: px(25),
109            position_type: PositionType::Absolute,
110            align_items: AlignItems::Center,
111            justify_content: JustifyContent::Center,
112        }
113        TabGroup
114        Children [
115            Node {
116                width: percent(80),
117                border: px(5),
118                padding: px(5),
119                flex_grow: 0.0,
120                border_radius: BorderRadius::all(px(10)),
121            }
122            BorderColor::from(WHITE)
123            TextInput
124            EditableText {
125                visible_lines: { Some(5.) }
126                allow_newlines: true,
127            }
128            TextLayout::no_wrap()
129            TextCursorStyle {
130                color: WHITE
131            }
132            TextFont {
133                font_size: FontSize::Px(25.),
134            }
135            TabIndex(0)
136            AutoFocus
137        ]
138    }
139}