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

This commit is contained in:
jkds
2026-01-03 02:04:52 +01:00
parent 02530cc0f4
commit b182fa7fa9
4 changed files with 42 additions and 29 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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());

View File

@@ -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;
}
}
}