diff --git a/bin/app/src/android/textinput/gametextinput.rs b/bin/app/src/android/textinput/gametextinput.rs index 0fc4cb050..e63e27301 100644 --- a/bin/app/src/android/textinput/gametextinput.rs +++ b/bin/app/src/android/textinput/gametextinput.rs @@ -182,21 +182,19 @@ impl GameTextInput { w!("push_update() - no input_connection set"); return }; - if let Some(input_connection) = *self.input_connection.read() { - unsafe { - let env = android::attach_jni_env(); - let jstate = self.state_to_java(state); - call_void_method!( - env, - input_connection, - "setState", - "(Ltextinput/State;)V", - jstate - ); + unsafe { + let env = android::attach_jni_env(); + let jstate = self.state_to_java(state); + call_void_method!( + env, + input_connection, + "setState", + "(Ltextinput/State;)V", + jstate + ); - let delete_local_ref = (**env).DeleteLocalRef.unwrap(); - delete_local_ref(env, jstate); - } + let delete_local_ref = (**env).DeleteLocalRef.unwrap(); + delete_local_ref(env, jstate); } } diff --git a/bin/app/src/android/textinput/mod.rs b/bin/app/src/android/textinput/mod.rs index 72e60b328..b41c19c4f 100644 --- a/bin/app/src/android/textinput/mod.rs +++ b/bin/app/src/android/textinput/mod.rs @@ -78,9 +78,11 @@ impl AndroidTextInput { // Always update our own state. let mut ours = self.state.lock(); ours.state = state.clone(); + let is_active = ours.is_active; + drop(ours); // Only update java state when this input is active - if ours.is_active { + if is_active { let gti = GAME_TEXT_INPUT.get().unwrap(); gti.push_update(&state); } diff --git a/bin/app/src/text2/editor/android.rs b/bin/app/src/text2/editor/android.rs index 1961292ea..6a4fc7321 100644 --- a/bin/app/src/text2/editor/android.rs +++ b/bin/app/src/text2/editor/android.rs @@ -81,9 +81,14 @@ impl Editor { self.refresh().await; } pub async fn on_buffer_changed(&mut self, atom: &mut PropertyAtomicGuard) { - // Refresh the layout using the Android buffer - self.refresh().await; + // Only refresh layout if text content actually changed + // Avoid triggering expensive recomputes of layout and property tree. + let old_text = self.text.get(); + if old_text == self.state.text { + return + } + self.refresh().await; // Update the text attribute self.text.set(atom, &self.state.text); } @@ -126,7 +131,7 @@ 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}"); - self.state.text = self.text.get(); + assert!(cursor_idx <= self.state.text.len()); self.state.select = (cursor_idx, cursor_idx); self.state.compose = None; self.input.set_state(self.state.clone()); diff --git a/bin/app/src/ui/edit/mod.rs b/bin/app/src/ui/edit/mod.rs index 5d7a7832a..a8bded8cb 100644 --- a/bin/app/src/ui/edit/mod.rs +++ b/bin/app/src/ui/edit/mod.rs @@ -1304,14 +1304,11 @@ impl BaseEdit { 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() || 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; + // Diff old and new state so we know what changed + let is_text_changed = editor.state.text != state.text; + let is_select_changed = editor.state.select != state.select; + let is_compose_changed = editor.state.compose != state.compose; editor.state = state; editor.on_buffer_changed(atom).await; drop(editor); @@ -1319,10 +1316,21 @@ impl BaseEdit { self.eval_rect().await; self.behave.apply_cursor_scroll().await; - // Only redraw once we have the parent_rect - // Can happen when we receive an Android event before the canvas is ready - if self.parent_rect.lock().is_some() { - self.redraw(atom).await; + // Text changed - finish any active selection + if is_text_changed || is_compose_changed { + self.pause_blinking(); + //assert!(state.text != self.text.get()); + self.finish_select(atom); + + // Only redraw once we have the parent_rect + // Can happen when we receive an Android event before the canvas is ready + if self.parent_rect.lock().is_some() { + self.redraw(atom).await; + } + } else if is_select_changed { + // Redrawing the entire text just for select changes is expensive + self.redraw_cursor(atom.batch_id).await; + self.redraw_select(atom.batch_id).await; } } }