diff --git a/src/changelog/v0.30.md b/src/changelog/v0.30.md index d6c6ef6b07..0f6460223c 100644 --- a/src/changelog/v0.30.md +++ b/src/changelog/v0.30.md @@ -1,3 +1,10 @@ + +## 0.30.13 + +### Added + +- On Android, added support for Ime events, for soft keyboard input. + ## 0.30.12 ### Fixed diff --git a/src/event.rs b/src/event.rs index 1890aea97d..f95c6f7968 100644 --- a/src/event.rs +++ b/src/event.rs @@ -227,7 +227,7 @@ pub enum WindowEvent { /// /// ## Platform-specific /// - /// - **iOS / Android / Web / Orbital:** Unsupported. + /// - **iOS / Web / Orbital:** Unsupported. Ime(Ime), /// The cursor has moved on the window. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index bc0ad680e5..4c659de676 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -6,7 +6,9 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Arc, Mutex}; use std::time::{Duration, Instant}; -use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction}; +use android_activity::input::{ + InputEvent, KeyAction, Keycode, MotionAction, TextInputState, TextSpan, +}; use android_activity::{ AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect, }; @@ -466,6 +468,31 @@ impl EventLoop { }, } }, + InputEvent::TextEvent(ime_state) => { + // Note: Winit does not support surrounding text or tracking a selection/cursor that + // may span within the surrounding text and the preedit text. + // + // Since there's no API to specify surrounding text, set_ime_allowed() will reset + // the text to an empty string and we will treat all the text as preedit text. + // + // We map Android's composing region to winit's preedit selection region. + // + // This seems a little odd, since Android's notion of a "composing region" would + // normally be equated with winit's "preedit" text but conceptually we're mapping + // Android's surrounding text + composing region into winit's preedit text + + // selection region. + // + // We ignore the separate selection region that Android supports. + let event = event::Event::WindowEvent { + window_id: window::WindowId(WindowId), + event: event::WindowEvent::Ime(event::Ime::Preedit( + ime_state.text.clone(), + ime_state.compose_region.map(|span| (span.start, span.end)), + )), + }; + + callback(event, self.window_target()); + }, _ => { warn!("Unknown android_activity input event {event:?}") }, @@ -770,6 +797,7 @@ pub struct PlatformSpecificWindowAttributes; pub(crate) struct Window { app: AndroidApp, redraw_requester: RedrawRequester, + ime_allowed: AtomicBool, } impl Window { @@ -779,7 +807,11 @@ impl Window { ) -> Result { // FIXME this ignores requested window attributes - Ok(Self { app: el.app.clone(), redraw_requester: el.redraw_requester.clone() }) + Ok(Self { + app: el.app.clone(), + redraw_requester: el.redraw_requester.clone(), + ime_allowed: AtomicBool::new(false), + }) } pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) { @@ -909,11 +941,24 @@ impl Window { pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {} pub fn set_ime_allowed(&self, allowed: bool) { + // Request a show/hide regardless of whether the state has changed, since + // the keyboard may have been dismissed by the user manually while in the + // middle of text input if allowed { self.app.show_soft_input(true); } else { self.app.hide_soft_input(true); } + + if self.ime_allowed.swap(allowed, Ordering::SeqCst) == allowed { + return; + } + + self.app.set_text_input_state(TextInputState { + text: String::new(), + selection: TextSpan { start: 0, end: 0 }, + compose_region: None, + }); } pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}