Skip to main content

ime_support/
ime_support.rs

1//! Demonstrates IME (Input Method Editor) support for text input.
2//!
3//! IME allows users to input characters that aren't directly on their keyboard,
4//! such as Chinese, Japanese, and Korean characters.
5//!
6//! To use IME input, the system must have fonts installed that support the target script.
7//! This example uses [`FontSource::SansSerif`], which resolves to a system sans-serif font.
8//! On systems without e.g. CJK fonts installed, CJK input will render as boxes or question marks.
9use bevy::color::palettes::css::DARK_GREY;
10use bevy::color::palettes::tailwind::SLATE_300;
11use bevy::input_focus::{
12    tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin},
13    InputFocus,
14};
15use bevy::prelude::*;
16use bevy::text::{EditableText, TextCursorStyle};
17use bevy::ui_widgets::TextInput;
18
19fn main() {
20    App::new()
21        .add_plugins(DefaultPlugins)
22        .add_plugins(TabNavigationPlugin)
23        .add_systems(Startup, setup)
24        .add_systems(Update, text_submission)
25        .run();
26}
27
28#[derive(Component)]
29struct TextOutput;
30
31fn setup(mut commands: Commands) {
32    commands.spawn(Camera2d);
33
34    let instructions = commands
35        .spawn((
36            Text::new("Type using your IME, then press Ctrl+Enter to submit. Your system default sans-serif font will be used, so make sure you have fonts installed that support the characters you want to input!"),
37            TextFont {
38                font_size: FontSize::Px(20.0),
39                ..default()
40            },
41        ))
42        .id();
43
44    let text_input = commands
45        .spawn((
46            Node {
47                width: px(400),
48                height: px(250),
49                border: px(3).all(),
50                padding: px(8).all(),
51                ..default()
52            },
53            // SansSerif resolves to a system sans-serif font, which on most CJK systems
54            // includes support for Chinese, Japanese, and Korean characters.
55            // Note that using system fonts requires the "bevy/system-fonts" feature to be enabled.
56            TextFont {
57                font: FontSource::sans_serif(),
58                font_size: FontSize::Px(32.0),
59                ..default()
60            },
61            BorderColor::from(Color::from(SLATE_300)),
62            TextInput,
63            EditableText {
64                allow_newlines: true,
65                ..default()
66            },
67            TextLayout::no_wrap(),
68            TextCursorStyle::default(),
69            TabIndex(0),
70            BackgroundColor(DARK_GREY.into()),
71        ))
72        .id();
73
74    let text_output = commands
75        .spawn((
76            Text::new("Your text here!"),
77            TextFont {
78                font: FontSource::sans_serif(),
79                font_size: FontSize::Px(32.0),
80                ..default()
81            },
82            TextOutput,
83        ))
84        .id();
85
86    commands
87        .spawn((
88            Node {
89                flex_direction: FlexDirection::Column,
90                padding: px(24.0).all(),
91                row_gap: px(16),
92                ..default()
93            },
94            TabGroup::new(0),
95        ))
96        .add_children(&[instructions, text_input, text_output]);
97}
98
99fn text_submission(
100    input_focus: Res<InputFocus>,
101    keyboard_input: Res<ButtonInput<KeyCode>>,
102    mut text_input: Query<&mut EditableText>,
103    mut text_output: Single<&mut Text, With<TextOutput>>,
104) {
105    if keyboard_input.just_pressed(KeyCode::Enter)
106        && (keyboard_input.pressed(KeyCode::ControlLeft)
107            || keyboard_input.pressed(KeyCode::ControlRight))
108        && let Some(focused_entity) = input_focus.get()
109        && let Ok(mut input) = text_input.get_mut(focused_entity)
110    {
111        text_output.0 = input.value().to_string();
112        input.clear();
113    }
114}