diff --git a/bin/app/src/android/textinput/gametextinput.rs b/bin/app/src/android/textinput/gametextinput.rs index 5ec5f1865..0fc4cb050 100644 --- a/bin/app/src/android/textinput/gametextinput.rs +++ b/bin/app/src/android/textinput/gametextinput.rs @@ -25,6 +25,7 @@ use std::{ use super::{AndroidTextInputState, SharedStatePtr}; +macro_rules! t { ($($arg:tt)*) => { trace!(target: "android::textinput::gametextinput", $($arg)*); } } macro_rules! w { ($($arg:tt)*) => { warn!(target: "android::textinput::gametextinput", $($arg)*); } } pub const SPAN_UNDEFINED: i32 = -1; @@ -213,6 +214,7 @@ impl GameTextInput { pub fn process_event(&self, event_state: ndk_sys::jobject) { let state = self.state_from_java(event_state); + t!("IME event: {state:?}"); let Some(shared) = &*self.state.lock() else { w!("process_event() - no shared state set"); diff --git a/bin/app/src/android/textinput/mod.rs b/bin/app/src/android/textinput/mod.rs index c47988a36..72e60b328 100644 --- a/bin/app/src/android/textinput/mod.rs +++ b/bin/app/src/android/textinput/mod.rs @@ -85,8 +85,4 @@ impl AndroidTextInput { gti.push_update(&state); } } - - pub fn get_state(&self) -> AndroidTextInputState { - self.state.lock().state.clone() - } } diff --git a/bin/app/src/text2/editor/android.rs b/bin/app/src/text2/editor/android.rs index 445e22f3b..1961292ea 100644 --- a/bin/app/src/text2/editor/android.rs +++ b/bin/app/src/text2/editor/android.rs @@ -32,6 +32,7 @@ macro_rules! t { ($($arg:tt)*) => { trace!(target: "text::editor::android", $($a pub struct Editor { input: AndroidTextInput, + pub state: AndroidTextInputState, pub recvr: async_channel::Receiver, layout: parley::Layout, @@ -54,9 +55,9 @@ impl Editor { ) -> Self { let (sender, recvr) = async_channel::unbounded(); let input = AndroidTextInput::new(sender); - Self { input, + state: Default::default(), recvr, layout: Default::default(), @@ -72,11 +73,10 @@ impl Editor { pub async fn on_text_prop_changed(&mut self) { // Update GameTextInput state - let mut state = self.input.get_state().clone(); - state.text = self.text.get(); - state.select = (0, 0); - state.compose = None; - self.input.set_state(state); + self.state.text = self.text.get(); + self.state.select = (0, 0); + self.state.compose = None; + self.input.set_state(self.state.clone()); // Refresh our layout self.refresh().await; } @@ -85,8 +85,7 @@ impl Editor { self.refresh().await; // Update the text attribute - let state = self.input.get_state(); - self.text.set(atom, &state.text); + self.text.set(atom, &self.state.text); } pub fn focus(&mut self) { @@ -102,16 +101,14 @@ impl Editor { let window_scale = self.window_scale.get(); let lineheight = self.lineheight.get(); - let state = self.input.get_state(); - let mut underlines = vec![]; - if let Some((compose_start, compose_end)) = state.compose { + if let Some((compose_start, compose_end)) = self.state.compose { underlines.push(compose_start..compose_end); } let mut txt_ctx = TEXT_CTX.get().await; self.layout = txt_ctx.make_layout( - &state.text, + &self.state.text, text_color, font_size, lineheight, @@ -129,11 +126,10 @@ impl Editor { let cursor = parley::Cursor::from_point(&self.layout, pos.x, pos.y); let cursor_idx = cursor.index(); t!(" move_to_pos: {cursor_idx}"); - let mut state = self.input.get_state().clone(); - state.text = self.text.get(); - state.select = (cursor_idx, cursor_idx); - state.compose = None; - self.input.set_state(state); + self.state.text = self.text.get(); + self.state.select = (cursor_idx, cursor_idx); + self.state.compose = None; + self.input.set_state(self.state.clone()); } pub async fn select_word_at_point(&mut self, pos: Point) { @@ -145,14 +141,12 @@ impl Editor { pub fn get_cursor_pos(&self) -> Point { let lineheight = self.lineheight.get(); - let state = self.input.get_state(); + let cursor_idx = self.state.select.0; - let cursor_idx = state.select.0; - - let cursor = if cursor_idx >= state.text.len() { + let cursor = if cursor_idx >= self.state.text.len() { parley::Cursor::from_byte_index( &self.layout, - state.text.len(), + self.state.text.len(), parley::Affinity::Upstream, ) } else { @@ -165,11 +159,11 @@ impl Editor { pub async fn insert(&mut self, txt: &str, atom: &mut PropertyAtomicGuard) { // TODO: need to verify this is correct // Insert text by updating the state - let mut current_state = self.input.get_state().clone(); - current_state.text.push_str(txt); - current_state.select = (current_state.text.len(), current_state.text.len()); - current_state.compose = None; - self.input.set_state(current_state); + self.state.text.push_str(txt); + let cursor_idx = self.state.text.len(); + self.state.select = (cursor_idx, cursor_idx); + self.state.compose = None; + self.input.set_state(self.state.clone()); self.on_buffer_changed(atom).await; } @@ -191,21 +185,21 @@ impl Editor { } pub fn selected_text(&self) -> Option { - let state = self.input.get_state(); - if state.select.0 == state.select.1 { + let (start, end) = (self.state.select.0, self.state.select.1); + if start == end { return None } - let (start, end) = - (min(state.select.0, state.select.1), max(state.select.0, state.select.1)); - Some(state.text[start..end].to_string()) + let (start, end) = (min(start, end), max(start, end)); + Some(self.state.text[start..end].to_string()) } pub fn selection(&self, side: isize) -> parley::Selection { assert!(side.abs() == 1); - let state = self.input.get_state(); + t!("selection({side}) [state={:?}]", self.state); + let (start, end) = (self.state.select.0, self.state.select.1); let (anchor, focus) = match side { - -1 => (state.select.1, state.select.0), - 1 => (state.select.0, state.select.1), + -1 => (end, start), + 1 => (start, end), _ => panic!(), }; @@ -217,15 +211,14 @@ impl Editor { parley::Selection::new(anchor, focus) } pub async fn set_selection(&mut self, select_start: usize, select_end: usize) { - let mut state = self.input.get_state().clone(); - state.text = self.text.get(); - state.select = (select_start, select_end); - state.compose = None; - self.input.set_state(state); + self.state.text = self.text.get(); + self.state.select = (select_start, select_end); + self.state.compose = None; + self.input.set_state(self.state.clone()); } #[allow(dead_code)] pub fn buffer(&self) -> String { - self.input.get_state().text.clone() + self.state.text.clone() } } diff --git a/bin/app/src/ui/edit/mod.rs b/bin/app/src/ui/edit/mod.rs index 662b86f2d..5d7a7832a 100644 --- a/bin/app/src/ui/edit/mod.rs +++ b/bin/app/src/ui/edit/mod.rs @@ -1300,16 +1300,19 @@ impl BaseEdit { if !self.is_active.get() { return } - t!("handle_android_event({state:?})"); + t!("handle_android_event({state:?})"); let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_android_event")); // Text changed - finish any active selection - if state.text != self.text.get() { + if state.text != self.text.get() || state.select.0 == state.select.1 { + // Safe to call before we update the editor. + // I just wanna avoid cloning state since we move it into editor. self.finish_select(atom); } let mut editor = self.lock_editor().await; + editor.state = state; editor.on_buffer_changed(atom).await; drop(editor);