Просмотр исходного кода

app: improve android editor perf and eliminate a deadlock in android text input

jkds 7 месяцев назад
Родитель
Сommit
b182fa7fa9

+ 13 - 15
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
-                );
-
-                let delete_local_ref = (**env).DeleteLocalRef.unwrap();
-                delete_local_ref(env, 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);
         }
     }
 

+ 3 - 1
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);
         }

+ 8 - 3
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());

+ 19 - 11
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;
         }
     }
 }