Преглед изворни кода

app: reduce JNI overhead when adjusting a selection by creating a special setSelection() fn so we dont have to pass the entire state over the bridge.

jkds пре 7 месеци
родитељ
комит
d131828285

+ 38 - 35
bin/app/java/textinput/InputConnection.java

@@ -45,7 +45,6 @@ import androidx.core.view.WindowInsetsCompat;
 import textinput.GameTextInput.Pair;
 
 public class InputConnection extends BaseInputConnection implements View.OnKeyListener {
-  private static final String TAG = "gti.InputConnection";
   private final InputMethodManager imm;
   private final View targetView;
   private final Settings settings;
@@ -53,6 +52,10 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
   private Listener listener;
   private boolean mSoftKeyboardActive;
 
+  private void log(String text) {
+    //Log.d("darkfi", text);
+  }
+
   /*
    * This class filters EOL characters from the input. For details of how InputFilter.filter
    * function works, refer to its documentation. If the suggested change is accepted without
@@ -100,7 +103,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
    */
   public InputConnection(Context ctx, View targetView, Settings settings) {
     super(targetView, settings.mEditorInfo.inputType != 0);
-    Log.d(TAG, "InputConnection created");
+    log("InputConnection created");
 
     this.targetView = targetView;
     this.settings = settings;
@@ -143,7 +146,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
    *     https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#showSoftInput(android.view.View,%20int)
    */
   public final void setSoftKeyboardActive(boolean active, int flags) {
-    Log.d(TAG, "setSoftKeyboardActive, active: " + active);
+    log("setSoftKeyboardActive, active: " + active);
 
     this.mSoftKeyboardActive = active;
     if (active) {
@@ -171,7 +174,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
    * @param editorInfo The EditorInfo to use
    */
   public final void setEditorInfo(EditorInfo editorInfo) {
-    Log.d(TAG, "setEditorInfo");
+    log("setEditorInfo");
     settings.mEditorInfo = editorInfo;
 
     // Depending on the multiline state, we might need a different set of filters.
@@ -195,7 +198,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
   public final void setState(State state) {
     if (state == null)
       return;
-    Log.d(TAG,
+    log(
         "setState: '" + state.text + "', selection=(" + state.selectionStart + ","
             + state.selectionEnd + "), composing region=(" + state.composingRegionStart + ","
             + state.composingRegionEnd + ")");
@@ -232,7 +235,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
   // From View.OnKeyListener
   @Override
   public boolean onKey(View view, int i, KeyEvent keyEvent) {
-    Log.d(TAG, "onKey: " + keyEvent);
+    log("onKey: " + keyEvent);
     if (!getSoftKeyboardActive()) {
       return false;
     }
@@ -251,22 +254,22 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
   // From BaseInputConnection
   @Override
   public Editable getEditable() {
-    Log.d(TAG, "getEditable");
+    log("getEditable");
     return mEditable;
   }
 
   // From BaseInputConnection
   @Override
   public boolean setSelection(int start, int end) {
-    Log.d(TAG, "setSelection: " + start + ":" + end);
+    log("setSelection: " + start + ":" + end);
     return super.setSelection(start, end);
   }
 
   // From BaseInputConnection
   @Override
   public boolean setComposingText(CharSequence text, int newCursorPosition) {
-    Log.d(
-        TAG, String.format("setComposingText='%s' newCursorPosition=%d", text, newCursorPosition));
+    log(
+        "setComposingText='" + text + "' newCursorPosition=" + newCursorPosition);
     if (text == null) {
       return false;
     }
@@ -275,40 +278,40 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
 
   @Override
   public boolean setComposingRegion(int start, int end) {
-    Log.d(TAG, "setComposingRegion: " + start + ":" + end);
+    log("setComposingRegion: " + start + ":" + end);
     return super.setComposingRegion(start, end);
   }
 
   // From BaseInputConnection
   @Override
   public boolean finishComposingText() {
-    Log.d(TAG, "finishComposingText");
+    log("finishComposingText");
     return super.finishComposingText();
   }
 
   @Override
   public boolean endBatchEdit() {
-    Log.d(TAG, "endBatchEdit");
+    log("endBatchEdit");
     stateUpdated();
     return super.endBatchEdit();
   }
 
   @Override
   public boolean commitCompletion(CompletionInfo text) {
-    Log.d(TAG, "commitCompletion");
+    log("commitCompletion");
     return super.commitCompletion(text);
   }
 
   @Override
   public boolean commitCorrection(CorrectionInfo text) {
-    Log.d(TAG, "commitCompletion");
+    log("commitCompletion");
     return super.commitCorrection(text);
   }
 
   // From BaseInputConnection
   @Override
   public boolean commitText(CharSequence text, int newCursorPosition) {
-    Log.d(TAG,
+      log(
         (new StringBuilder())
             .append("commitText: ")
             .append(text)
@@ -321,21 +324,21 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
   // From BaseInputConnection
   @Override
   public boolean deleteSurroundingText(int beforeLength, int afterLength) {
-    Log.d(TAG, "deleteSurroundingText: " + beforeLength + ":" + afterLength);
+    log("deleteSurroundingText: " + beforeLength + ":" + afterLength);
     return super.deleteSurroundingText(beforeLength, afterLength);
   }
 
   // From BaseInputConnection
   @Override
   public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
-    Log.d(TAG, "deleteSurroundingTextInCodePoints: " + beforeLength + ":" + afterLength);
+    log("deleteSurroundingTextInCodePoints: " + beforeLength + ":" + afterLength);
     return super.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
   }
 
   // From BaseInputConnection
   @Override
   public boolean sendKeyEvent(KeyEvent event) {
-    Log.d(TAG, "sendKeyEvent: " + event);
+    log("sendKeyEvent: " + event);
     return super.sendKeyEvent(event);
   }
 
@@ -346,16 +349,16 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
     if (result == null) {
       result = "";
     }
-    Log.d(TAG, "getSelectedText: " + flags + ", result: " + result);
+    log("getSelectedText: " + flags + ", result: " + result);
     return result;
   }
 
   // From BaseInputConnection
   @Override
   public CharSequence getTextAfterCursor(int length, int flags) {
-    Log.d(TAG, "getTextAfterCursor: " + length + ":" + flags);
+    log("getTextAfterCursor: " + length + ":" + flags);
     if (length < 0) {
-      Log.i(TAG, "getTextAfterCursor: returning null to due to an invalid length=" + length);
+      Log.i("darkfi", "getTextAfterCursor: returning null to due to an invalid length=" + length);
       return null;
     }
     return super.getTextAfterCursor(length, flags);
@@ -364,9 +367,9 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
   // From BaseInputConnection
   @Override
   public CharSequence getTextBeforeCursor(int length, int flags) {
-    Log.d(TAG, "getTextBeforeCursor: " + length + ", flags=" + flags);
+    log("getTextBeforeCursor: " + length + ", flags=" + flags);
     if (length < 0) {
-      Log.i(TAG, "getTextBeforeCursor: returning null to due to an invalid length=" + length);
+      Log.i("darkfi", "getTextBeforeCursor: returning null to due to an invalid length=" + length);
       return null;
     }
     return super.getTextBeforeCursor(length, flags);
@@ -375,39 +378,39 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
   // From BaseInputConnection
   @Override
   public boolean requestCursorUpdates(int cursorUpdateMode) {
-    Log.d(TAG, "Request cursor updates: " + cursorUpdateMode);
+    log("Request cursor updates: " + cursorUpdateMode);
     return super.requestCursorUpdates(cursorUpdateMode);
   }
 
   // From BaseInputConnection
   @Override
   public void closeConnection() {
-    Log.d(TAG, "closeConnection");
+    log("closeConnection");
     super.closeConnection();
   }
 
   @Override
   public boolean setImeConsumesInput(boolean imeConsumesInput) {
-    Log.d(TAG, "setImeConsumesInput: " + imeConsumesInput);
+    log("setImeConsumesInput: " + imeConsumesInput);
     return super.setImeConsumesInput(imeConsumesInput);
   }
 
   @Override
   public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
-    Log.d(TAG, "getExtractedText");
+    log("getExtractedText");
     return super.getExtractedText(request, flags);
   }
 
   @Override
   public boolean performPrivateCommand(String action, Bundle data) {
-    Log.d(TAG, "performPrivateCommand");
+    log("performPrivateCommand");
     return super.performPrivateCommand(action, data);
   }
 
   private void immUpdateSelection() {
     Pair selection = this.getSelection();
     Pair cr = this.getComposingRegion();
-    Log.d(TAG,
+    log(
         "immUpdateSelection: " + selection.first + "," + selection.second + ". " + cr.first + ","
             + cr.second);
     settings.mEditorInfo.initialSelStart = selection.first;
@@ -428,8 +431,8 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
       return false;
     }
     int keyCode = event.getKeyCode();
-    Log.d(
-        TAG, String.format("processKeyEvent(key=%d) text=%s", keyCode, this.mEditable.toString()));
+    log(
+        "processKeyEvent(key=" + keyCode + ") text=" + this.mEditable.toString());
     // Filter out Enter keys if multi-line mode is disabled.
     if ((settings.mEditorInfo.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == 0
         && (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER)
@@ -503,7 +506,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
     }
 
     if (selection.first != selection.second) {
-      Log.d(TAG, String.format("processKeyEvent: deleting selection"));
+      log("processKeyEvent: deleting selection");
       this.mEditable.delete(selection.first, selection.second);
     }
 
@@ -525,7 +528,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
     this.setComposingRegion(composingRegion.first, composingRegion.second);
     int new_cursor = selection.first + charsToInsert.length();
     setSelection(new_cursor, new_cursor);
-    Log.d(TAG, String.format("processKeyEvent: exit, text=%s", this.mEditable.toString()));
+    log("processKeyEvent: exit, text=" + this.mEditable.toString());
     return true;
   }
 
@@ -594,7 +597,7 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
    */
   @Override
   public boolean performEditorAction(int action) {
-    Log.d(TAG, "performEditorAction, action=" + action);
+    log("performEditorAction, action=" + action);
     if (action == IME_ACTION_UNSPECIFIED) {
       // Super emulates Enter key press/release
       return super.performEditorAction(action);

+ 19 - 1
bin/app/src/android/textinput/gametextinput.rs

@@ -173,8 +173,11 @@ impl GameTextInput {
         let mut new_focus = state.lock();
         // Mark new state as active
         new_focus.is_active = true;
+        let new_state = new_focus.state.clone();
+        drop(new_focus);
+
         // Push changes to the Java side
-        self.push_update(&new_focus.state);
+        self.push_update(&new_state);
     }
 
     pub fn push_update(&self, state: &AndroidTextInputState) {
@@ -192,6 +195,21 @@ impl GameTextInput {
         }
     }
 
+    pub fn set_select(&self, start: i32, end: i32) -> Result<(), ()> {
+        let Some(input_connection) = *self.input_connection.read() else {
+            w!("push_update() - no input_connection set");
+            return Err(())
+        };
+        let is_success = unsafe {
+            let env = android::attach_jni_env();
+            call_bool_method!(env, input_connection, "setSelection", "(II)Z", start, end)
+        };
+        if is_success == 0u8 {
+            return Err(())
+        }
+        Ok(())
+    }
+
     pub fn set_input_connection(&self, input_connection: ndk_sys::jobject) {
         unsafe {
             let env = android::attach_jni_env();

+ 18 - 0
bin/app/src/android/textinput/mod.rs

@@ -87,4 +87,22 @@ impl AndroidTextInput {
             gti.push_update(&state);
         }
     }
+
+    pub fn set_select(&self, select_start: usize, select_end: usize) {
+        //t!("set_select({select_start}, {select_end})");
+        // Always update our own state.
+        let mut ours = self.state.lock();
+        let state = &mut ours.state;
+        assert!(select_start <= state.text.len());
+        assert!(select_end <= state.text.len());
+        state.select = (select_start, select_end);
+        let is_active = ours.is_active;
+        drop(ours);
+
+        // Only update java state when this input is active
+        if is_active {
+            let gti = GAME_TEXT_INPUT.get().unwrap();
+            gti.set_select(select_start as i32, select_end as i32).unwrap();
+        }
+    }
 }

+ 10 - 4
bin/app/src/app/schema/chat.rs

@@ -1559,13 +1559,19 @@ pub async fn make(
                 let atom = &mut render_api.make_guard(gfxtag!("edit select task"));
                 if editz_select_text.is_null(0).unwrap() {
                     info!(target: "app::chat", "selection changed: null");
-                    actions_is_visible.set(atom, false);
-                    pasta_is_visible2.set(atom, false);
+                    // Avoid triggering unecessary redraws
+                    if actions_is_visible.get() {
+                        actions_is_visible.set(atom, false);
+                        pasta_is_visible2.set(atom, false);
+                    }
                 } else {
                     let select_text = editz_select_text.get_str(0).unwrap();
                     info!(target: "app::chat", "selection changed: {select_text}");
-                    actions_is_visible.set(atom, true);
-                    pasta_is_visible2.set(atom, false);
+                    // Avoid triggering unecessary redraws
+                    if !actions_is_visible.get() {
+                        actions_is_visible.set(atom, true);
+                        pasta_is_visible2.set(atom, false);
+                    }
                 }
             }
         });

+ 14 - 6
bin/app/src/app/schema/mod.rs

@@ -529,15 +529,23 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
 
     let chatdb_path = get_chatdb_path();
     let db = sled::open(chatdb_path).expect("cannot open sleddb");
-    //for channel in CHANNELS {
-    chat::make(app, content.clone(), "dev", &db, i18n_fish, emoji_meshes.clone(), is_first_time)
+    for channel in CHANNELS {
+        chat::make(
+            app,
+            content.clone(),
+            channel,
+            &db,
+            i18n_fish,
+            emoji_meshes.clone(),
+            is_first_time,
+        )
         .await;
-    //}
-    //menu::make(app, content.clone(), i18n_fish).await;
+    }
+    menu::make(app, content.clone(), i18n_fish).await;
 
     // @@@ Debug stuff @@@
-    let chatview_node = app.sg_root.lookup_node("/window/content/dev_chat_layer").unwrap();
-    chatview_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    //let chatview_node = app.sg_root.lookup_node("/window/content/dev_chat_layer").unwrap();
+    //chatview_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
     //let menu_node = app.sg_root.lookup_node("/window/content/menu_layer").unwrap();
     //menu_node.set_property_bool(atom, Role::App, "is_visible", false).unwrap();
 }

+ 6 - 3
bin/app/src/text2/editor/android.rs

@@ -132,9 +132,10 @@ impl Editor {
         let cursor_idx = cursor.index();
         t!("  move_to_pos: {cursor_idx}");
         assert!(cursor_idx <= self.state.text.len());
+        assert_eq!(self.state.text, self.text.get());
         self.state.select = (cursor_idx, cursor_idx);
         self.state.compose = None;
-        self.input.set_state(self.state.clone());
+        self.input.set_select(cursor_idx, cursor_idx);
     }
 
     pub async fn select_word_at_point(&mut self, pos: Point) {
@@ -216,10 +217,12 @@ impl Editor {
         parley::Selection::new(anchor, focus)
     }
     pub async fn set_selection(&mut self, select_start: usize, select_end: usize) {
-        self.state.text = self.text.get();
+        assert!(select_start <= self.state.text.len());
+        assert!(select_end <= self.state.text.len());
+        assert_eq!(self.state.text, self.text.get());
         self.state.select = (select_start, select_end);
         self.state.compose = None;
-        self.input.set_state(self.state.clone());
+        self.input.set_select(select_start, select_end);
     }
 
     #[allow(dead_code)]

+ 5 - 4
bin/app/src/ui/edit/mod.rs

@@ -1363,16 +1363,17 @@ impl UIObject for BaseEdit {
     fn init(&self) {
         let mut guard = self.editor.lock_blocking();
         assert!(guard.is_none());
-        let mut editor = Editor::new(
+        let 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());
+        // For Android you can do this:
+        //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);
     }