app/edit: improve perf of editing selection. only recalc rect when editing text itself (not select changes)

This commit is contained in:
jkds
2026-01-03 06:36:44 +01:00
parent 737ba095f9
commit 7d589fd2ac
6 changed files with 23 additions and 20 deletions

View File

@@ -185,13 +185,7 @@ impl GameTextInput {
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
);
call_void_method!(env, input_connection, "setState", "(Ltextinput/State;)V", jstate);
let delete_local_ref = (**env).DeleteLocalRef.unwrap();
delete_local_ref(env, jstate);

View File

@@ -32,5 +32,7 @@ unsafe fn check_except(env: *mut ndk_sys::JNIEnv, context: &str) {
let exception_clear = (**env).ExceptionClear.unwrap();
exception_clear(env);
panic!("Java exception detected in {context}");
}
}

View File

@@ -199,7 +199,7 @@ impl Editor {
}
pub fn selection(&self, side: isize) -> parley::Selection {
assert!(side.abs() == 1);
t!("selection({side}) [state={:?}]", self.state);
//t!("selection({side}) [state={:?}]", self.state);
let (start, end) = (self.state.select.0, self.state.select.1);
let (anchor, focus) = match side {

View File

@@ -1003,7 +1003,7 @@ impl UIObject for ChatView {
}
let rect = self.rect.get();
t!("handle_touch({phase:?}, {id},{id}, {touch_pos:?})");
//t!("handle_touch({phase:?}, {id},{id}, {touch_pos:?})");
let atom = &mut self.render_api.make_guard(gfxtag!("ChatView::handle_touch"));
let touch_y = touch_pos.y;

View File

@@ -857,7 +857,7 @@ impl BaseEdit {
}
true
}
async fn handle_touch_end(&self, atom: &mut PropertyAtomicGuard, mut touch_pos: Point) -> bool {
async fn handle_touch_end(&self, mut touch_pos: Point) -> bool {
//t!("handle_touch_end({touch_pos:?})");
self.abs_to_local(&mut touch_pos);
@@ -865,6 +865,7 @@ impl BaseEdit {
match state {
TouchStateAction::Inactive => return false,
TouchStateAction::Started { pos: _, instant: _ } | TouchStateAction::SetCursorPos => {
let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_touch_end"));
self.touch_set_cursor_pos(atom, touch_pos).await;
self.redraw(atom).await;
}
@@ -980,7 +981,7 @@ impl BaseEdit {
editor.selected_text()
};
//d!("Select {seltext:?} from {clip_mouse_pos:?} (unclipped: {mouse_pos:?}) to ({sel_start}, {sel_end})");
//d!("Select {seltext:?} from {clip_mouse_pos:?} (unclipped: {mouse_pos:?})");
// Android editor impl detail: selection disappears when anchor == index
// But we disallow this so it should never happen. Just making a note of it here.
@@ -1047,6 +1048,7 @@ impl BaseEdit {
}
async fn redraw_select(&self, batch_id: BatchGuardId) {
//t!("redraw_select");
let sel_instrs = self.regen_select_mesh().await;
let phone_sel_instrs = self.regen_phone_select_handle_mesh().await;
let draw_calls = vec![
@@ -1315,13 +1317,10 @@ impl BaseEdit {
// Nothing changed. Just return.
if !is_text_changed && !is_select_changed && !is_compose_changed {
t!("Skipping update since nothing changed");
//t!("Skipping update since nothing changed");
return
}
self.eval_rect().await;
self.behave.apply_cursor_scroll().await;
//t!("is_text_changed={is_text_changed}, is_select_changed={is_select_changed}, is_compose_changed={is_compose_changed}");
// Only redraw once we have the parent_rect
// Can happen when we receive an Android event before the canvas is ready
@@ -1331,6 +1330,9 @@ impl BaseEdit {
// Text changed - finish any active selection
if is_text_changed || is_compose_changed {
self.eval_rect().await;
self.behave.apply_cursor_scroll().await;
self.pause_blinking();
//assert!(state.text != self.text.get());
self.finish_select(atom);
@@ -1338,6 +1340,7 @@ impl BaseEdit {
} else if is_select_changed {
// Redrawing the entire text just for select changes is expensive
self.redraw_cursor(atom.batch_id).await;
//t!("handle_android_event calling redraw_select");
self.redraw_select(atom.batch_id).await;
}
}
@@ -1360,13 +1363,17 @@ impl UIObject for BaseEdit {
fn init(&self) {
let mut guard = self.editor.lock_blocking();
assert!(guard.is_none());
*guard = Some(Editor::new(
let mut editor = Editor::new(
self.text.clone(),
self.font_size.clone(),
self.text_color.clone(),
self.window_scale.clone(),
self.lineheight.clone(),
));
);
let atom = &mut PropertyAtomicGuard::none();
self.text.set(atom, "the quick brown fox jumped over the");
smol::block_on(editor.on_text_prop_changed());
*guard = Some(editor);
}
async fn start(self: Arc<Self>, ex: ExecutorPtr) {
@@ -1758,12 +1765,10 @@ impl UIObject for BaseEdit {
return false
}
let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_touch"));
match phase {
TouchPhase::Started => self.handle_touch_start(touch_pos).await,
TouchPhase::Moved => self.handle_touch_move(touch_pos).await,
TouchPhase::Ended => self.handle_touch_end(atom, touch_pos).await,
TouchPhase::Ended => self.handle_touch_end(touch_pos).await,
TouchPhase::Cancelled => false,
}
}

View File

@@ -65,8 +65,10 @@ macro_rules! t { ($($arg:tt)*) => { trace!(target: "scene::on_modify", $($arg)*)
pub trait UIObject: Sync {
fn priority(&self) -> u32;
/// Called after schema and scenegraph is init but before miniquad starts.
fn init(&self) {}
/// Done after miniquad has started and the first window draw has been done.
async fn start(self: Arc<Self>, _ex: ExecutorPtr) {}
/// Clear all buffers and caches