Skip to content
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 by Bors] - add Input Method Editor support #7325

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
add example with simple text input
  • Loading branch information
mockersf committed Jan 22, 2023
commit 135e45af31c650fc79ae415287f7ec1ffcfe00e1
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,16 @@ description = "Prints out all touch inputs"
category = "Input"
wasm = false

[[example]]
name = "text_input"
path = "examples/input/text_input.rs"

[package.metadata.example.text_input]
name = "Text Input"
description = "Simple text input with IME support"
category = "Input"
wasm = false

# Reflection
[[example]]
name = "reflection"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ Example | Description
[Mouse Grab](../examples/input/mouse_grab.rs) | Demonstrates how to grab the mouse, locking the cursor to the app's screen
[Mouse Input](../examples/input/mouse_input.rs) | Demonstrates handling a mouse button press/release
[Mouse Input Events](../examples/input/mouse_input_events.rs) | Prints out all mouse events (buttons, movement, etc.)
[Text Input](../examples/input/text_input.rs) | Simple text input with IME support
[Touch Input](../examples/input/touch_input.rs) | Displays touch presses, releases, and cancels
[Touch Input Events](../examples/input/touch_input_events.rs) | Prints out all touch inputs

Expand Down
201 changes: 201 additions & 0 deletions examples/input/text_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
//! Simple text input support
//!
//! Return creates a new line, backspace removes the last character.
//! Clicking toggle IME (Input Method Editor) support, but the font used as limited support of characters.
//! You should change the provided font with another one to test other languages input.

use bevy::{input::keyboard::KeyboardInput, prelude::*};

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup_scene)
.add_system(toggle_ime)
.add_system(listen_ime_events)
.add_system(listen_received_character_events)
.add_system(listen_keyboard_input_events)
.add_system(bubbling_text)
.run();
}

fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2dBundle::default());

let font = asset_server.load("fonts/FiraMono-Medium.ttf");

commands.spawn(
TextBundle::from_sections([
TextSection {
value: "IME Enabled: ".to_string(),
style: TextStyle {
font: font.clone_weak(),
font_size: 20.0,
color: Color::WHITE,
},
},
TextSection {
value: "false\n".to_string(),
style: TextStyle {
font: font.clone_weak(),
font_size: 30.0,
color: Color::WHITE,
},
},
TextSection {
value: "IME Active: ".to_string(),
style: TextStyle {
font: font.clone_weak(),
font_size: 20.0,
color: Color::WHITE,
},
},
TextSection {
value: "false\n".to_string(),
style: TextStyle {
font: font.clone_weak(),
font_size: 30.0,
color: Color::WHITE,
},
},
TextSection {
value: "click to toggle IME, press return to start a new line\n\n".to_string(),
style: TextStyle {
font: font.clone_weak(),
font_size: 18.0,
color: Color::WHITE,
},
},
TextSection {
value: "".to_string(),
style: TextStyle {
font,
font_size: 25.0,
color: Color::WHITE,
},
},
])
.with_style(Style {
position_type: PositionType::Absolute,
position: UiRect {
top: Val::Px(10.0),
left: Val::Px(10.0),
..default()
},
..default()
}),
);

commands.spawn(Text2dBundle {
text: Text::from_section(
"".to_string(),
TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 100.0,
color: Color::WHITE,
},
),
..default()
});
}

fn toggle_ime(
input: Res<Input<MouseButton>>,
mut windows: Query<&mut Window>,
mut text: Query<&mut Text, With<Node>>,
) {
if input.just_pressed(MouseButton::Left) {
let mut window = windows.single_mut();

window.ime_position = window
.cursor_position()
.map(|p| Vec2::new(p.x, window.height() - p.y))
.unwrap();
window.ime_enabled = !window.ime_enabled;

let mut text = text.single_mut();
text.sections[1].value = format!("{}\n", window.ime_enabled);
}
}

#[derive(Component)]
struct Bubble {
timer: Timer,
}

#[derive(Component)]
struct ImePreedit;

fn bubbling_text(
mut commands: Commands,
mut bubbles: Query<(Entity, &mut Transform, &mut Bubble)>,
time: Res<Time>,
) {
for (entity, mut transform, mut bubble) in bubbles.iter_mut() {
if bubble.timer.tick(time.delta()).just_finished() {
commands.entity(entity).despawn();
}
transform.translation.y += time.delta_seconds() * 100.0;
}
}

fn listen_ime_events(
mut events: EventReader<Ime>,
mut status_text: Query<&mut Text, With<Node>>,
mut edit_text: Query<&mut Text, (Without<Node>, Without<Bubble>)>,
) {
for event in events.iter() {
match event {
Ime::Preedit { value, cursor, .. } if !cursor.is_none() => {
status_text.single_mut().sections[5].value = format!("IME buffer: {value}");
}
Ime::Preedit { cursor, .. } if cursor.is_none() => {
status_text.single_mut().sections[5].value = "".to_string();
}
Ime::Commit { value, .. } => {
edit_text.single_mut().sections[0].value.push_str(value);
}
Ime::Enabled { .. } => {
status_text.single_mut().sections[3].value = "true\n".to_string();
}
Ime::Disabled { .. } => {
status_text.single_mut().sections[3].value = "false\n".to_string();
}
_ => (),
}
}
}

fn listen_received_character_events(
mut events: EventReader<ReceivedCharacter>,
mut edit_text: Query<&mut Text, (Without<Node>, Without<Bubble>)>,
) {
for event in events.iter() {
edit_text.single_mut().sections[0].value.push(event.char);
}
}

fn listen_keyboard_input_events(
mut commands: Commands,
mut events: EventReader<KeyboardInput>,
mut edit_text: Query<(Entity, &mut Text), (Without<Node>, Without<Bubble>)>,
) {
for event in events.iter() {
match event.key_code {
Some(KeyCode::Return) => {
let (entity, text) = edit_text.single();
commands.entity(entity).insert(Bubble {
timer: Timer::from_seconds(5.0, TimerMode::Once),
});

commands.spawn(Text2dBundle {
text: Text::from_section("".to_string(), text.sections[0].style.clone()),
..default()
});
}
Some(KeyCode::Back) => {
edit_text.single_mut().1.sections[0].value.pop();
}
_ => continue,
}
}
}