diff --git a/src/lib.rs b/src/lib.rs index d1745605..ac635180 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -323,6 +323,7 @@ pub struct EguiContext { ctx: egui::Context, mouse_position: egui::Pos2, pointer_touch_id: Option, + has_sent_ime_enabled: bool, } impl EguiContext { @@ -363,7 +364,7 @@ type EguiContextsFilter = With; type EguiContextsFilter = Or<(With, With)>; #[derive(SystemParam)] -/// A helper SystemParam that provides a way to get `[EguiContext]` with less boilerplate and +/// A helper SystemParam that provides a way to get [`EguiContext`] with less boilerplate and /// combines a proxy interface to the [`EguiUserTextures`] resource. pub struct EguiContexts<'w, 's> { q: Query< @@ -380,7 +381,7 @@ pub struct EguiContexts<'w, 's> { user_textures: ResMut<'w, EguiUserTextures>, } -impl<'w, 's> EguiContexts<'w, 's> { +impl EguiContexts<'_, '_> { /// Egui context of the primary window. #[must_use] pub fn ctx_mut(&mut self) -> &mut egui::Context { @@ -809,6 +810,26 @@ pub struct EguiContextQuery { pub render_to_texture: Option<&'static mut EguiRenderToTextureHandle>, } +impl EguiContextQueryItem<'_> { + fn ime_event_enable(&mut self) { + if !self.ctx.has_sent_ime_enabled { + self.egui_input + .events + .push(egui::Event::Ime(egui::ImeEvent::Enabled)); + self.ctx.has_sent_ime_enabled = true; + } + } + + fn ime_event_disable(&mut self) { + if self.ctx.has_sent_ime_enabled { + self.egui_input + .events + .push(egui::Event::Ime(egui::ImeEvent::Disabled)); + self.ctx.has_sent_ime_enabled = false; + } + } +} + /// Contains textures allocated and painted by Egui. #[cfg(feature = "render")] #[derive(bevy::ecs::system::Resource, Deref, DerefMut, Default)] diff --git a/src/systems.rs b/src/systems.rs index d9b54c05..b391dba0 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -22,7 +22,7 @@ use bevy::{ log::{self, error}, prelude::{Entity, EventReader, NonSend, Query, Resource, Time}, time::Real, - window::{CursorMoved, RequestRedraw}, + window::{CursorMoved, Ime, RequestRedraw}, winit::{EventLoopProxy, WakeUp}, }; use std::{marker::PhantomData, time::Duration}; @@ -37,9 +37,10 @@ pub struct InputEvents<'w, 's> { pub ev_keyboard_input: EventReader<'w, 's, KeyboardInput>, pub ev_touch: EventReader<'w, 's, TouchInput>, pub ev_focus: EventReader<'w, 's, KeyboardFocusLost>, + pub ev_ime_input: EventReader<'w, 's, Ime>, } -impl<'w, 's> InputEvents<'w, 's> { +impl InputEvents<'_, '_> { /// Consumes all the events. pub fn clear(&mut self) { self.ev_cursor.clear(); @@ -48,6 +49,7 @@ impl<'w, 's> InputEvents<'w, 's> { self.ev_keyboard_input.clear(); self.ev_touch.clear(); self.ev_focus.clear(); + self.ev_ime_input.clear(); } } @@ -84,7 +86,7 @@ pub struct ContextSystemParams<'w, 's> { _marker: PhantomData<&'s ()>, } -impl<'w, 's> ContextSystemParams<'w, 's> { +impl ContextSystemParams<'_, '_> { fn window_context(&mut self, window: Entity) -> Option { match self.contexts.get_mut(window) { Ok(context) => Some(context), @@ -240,6 +242,47 @@ pub fn process_input_system( }); } + for event in input_events.ev_ime_input.read() { + let window = match &event { + Ime::Preedit { window, .. } + | Ime::Commit { window, .. } + | Ime::Disabled { window } + | Ime::Enabled { window } => *window, + }; + + let Some(mut window_context) = context_params.window_context(window) else { + continue; + }; + + // Aligned with the egui-winit implementation: https://github.com/emilk/egui/blob/0f2b427ff4c0a8c68f6622ec7d0afb7ba7e71bba/crates/egui-winit/src/lib.rs#L348 + match event { + Ime::Enabled { window: _ } => { + window_context.ime_event_enable(); + } + Ime::Preedit { + value, + window: _, + cursor: _, + } => { + window_context.ime_event_enable(); + window_context + .egui_input + .events + .push(egui::Event::Ime(egui::ImeEvent::Preedit(value.clone()))); + } + Ime::Commit { value, window: _ } => { + window_context + .egui_input + .events + .push(egui::Event::Ime(egui::ImeEvent::Commit(value.clone()))); + window_context.ime_event_disable(); + } + Ime::Disabled { window: _ } => { + window_context.ime_event_disable(); + } + } + } + for event in keyboard_input_events { let text_event_allowed = !command && !win || !*context_params.is_macos && ctrl && alt; let Some(mut window_context) = context_params.window_context(event.window) else {