Pārlūkot izejas kodu

app: make the android input type a scenegraph property

See: https://developer.android.com/reference/android/text/InputType
epiphany 2 mēneši atpakaļ
vecāks
revīzija
ef5b780b3b

+ 11 - 0
bin/app/java/textinput/InputConnection.java

@@ -186,6 +186,17 @@ public class InputConnection extends BaseInputConnection implements View.OnKeyLi
     }
   }
 
+  /**
+   * Set the input type for the IME.
+   *
+   * @param inputType The input type to use.
+   */
+  public final void setInputType(int inputType) {
+    log("setInputType: " + inputType);
+    settings.mEditorInfo.inputType = inputType;
+    restartInput();
+  }
+
   /**
    * Set the text, selection and composing region state.
    *

+ 27 - 0
bin/app/src/android/textinput/gametextinput.rs

@@ -49,6 +49,7 @@ pub struct GameTextInput {
     input_connection_class: ndk_sys::jclass,
     state_class: ndk_sys::jclass,
     set_soft_keyboard_active_method: ndk_sys::jmethodID,
+    set_input_type_method: ndk_sys::jmethodID,
     //restart_input_method: ndk_sys::jmethodID,
     state_constructor: ndk_sys::jmethodID,
     state_class_info: StateClassInfo,
@@ -88,6 +89,14 @@ impl GameTextInput {
                 set_soft_keyboard_active_sig.as_ptr() as _,
             );
 
+            let set_input_type_sig = b"(I)V\0";
+            let set_input_type_method = get_method_id(
+                env,
+                input_connection_class,
+                b"setInputType\0".as_ptr() as _,
+                set_input_type_sig.as_ptr() as _,
+            );
+
             /*let restart_input_sig = b"()V\0";
             let restart_input_method = get_method_id(
                 env,
@@ -153,6 +162,7 @@ impl GameTextInput {
                 input_connection_class,
                 state_class,
                 set_soft_keyboard_active_method,
+                set_input_type_method,
                 //restart_input_method,
                 state_constructor,
                 state_class_info,
@@ -272,6 +282,23 @@ impl GameTextInput {
         }
     }
 
+    pub fn set_input_type(&self, input_type: u32) {
+        let Some(input_connection) = *self.input_connection.read() else {
+            w!("set_input_type() - no input_connection set");
+            return
+        };
+        unsafe {
+            let env = get_jni_env();
+            let call_void_method = (**env).CallVoidMethod.unwrap();
+            call_void_method(
+                env,
+                input_connection,
+                self.set_input_type_method,
+                input_type as ndk_sys::jint,
+            );
+        }
+    }
+
     /*pub fn restart_input(&self) {
         let Some(input_connection) = *self.input_connection.read() else {
             w!("restart_input() - no input_connection set");

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

@@ -27,6 +27,24 @@ use gametextinput::GAME_TEXT_INPUT;
 
 macro_rules! t { ($($arg:tt)*) => { trace!(target: "android::textinput", $($arg)*); } }
 
+/// Android input type constants
+/// See: https://developer.android.com/reference/android/text/InputType
+pub mod input_types {
+    /// Base classes
+    pub const CLASS_TEXT: u32 = 0x00000001;
+    pub const CLASS_NUMBER: u32 = 0x00000002;
+    pub const CLASS_PHONE: u32 = 0x00000003;
+    pub const CLASS_DATETIME: u32 = 0x00000004;
+
+    /// Text flags
+    pub const TEXT_FLAG_AUTO_CORRECT: u32 = 0x00008000;
+    pub const TEXT_FLAG_MULTI_LINE: u32 = 0x00020000;
+
+    /// Number flags
+    pub const NUMBER_FLAG_DECIMAL: u32 = 0x00002000;
+    pub const NUMBER_FLAG_SIGNED: u32 = 0x00001000;
+}
+
 // Text input state exposed to the rest of the app
 #[derive(Debug, Clone, Default)]
 pub struct AndroidTextInputState {
@@ -73,6 +91,12 @@ impl AndroidTextInput {
         gti.hide_ime(0);
     }
 
+    pub fn set_input_type(&self, input_type: u32) {
+        t!("set_input_type({input_type})");
+        let gti = GAME_TEXT_INPUT.get().unwrap();
+        gti.set_input_type(input_type);
+    }
+
     pub fn set_state(&self, state: AndroidTextInputState) {
         t!("set_state({state:?})");
         // Always update our own state.

+ 35 - 0
bin/app/src/app/node.rs

@@ -21,6 +21,9 @@ use crate::{
     scene::{CallArgType, SceneNode, SceneNodeType},
 };
 
+#[cfg(target_os = "android")]
+use crate::prop::{PropertyAtomicGuard, Role};
+
 pub fn create_window(name: &str) -> SceneNode {
     let mut node = SceneNode::new(name, SceneNodeType::Window);
 
@@ -390,6 +393,14 @@ pub fn create_baseedit(name: &str) -> SceneNode {
     let prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null);
     node.add_property(prop).unwrap();
 
+    let mut prop = Property::new("android_input_type", PropertyType::Uint32, PropertySubType::Null);
+    #[cfg(target_os = "android")]
+    {
+        use crate::android::textinput::input_types::*;
+        prop.set_defaults_u32(vec![CLASS_TEXT | TEXT_FLAG_AUTO_CORRECT]).unwrap();
+    }
+    node.add_property(prop).unwrap();
+
     let mut prop = Property::new("action_fg_color", PropertyType::Float32, PropertySubType::Color);
     prop.set_ui_text("Action Menu FG Color", "Foreground color of action menu items");
     prop.set_array_len(4);
@@ -436,6 +447,15 @@ pub fn create_singleline_edit(name: &str) -> SceneNode {
 pub fn create_multiline_edit(name: &str) -> SceneNode {
     let mut node = create_baseedit(name);
 
+    #[cfg(target_os = "android")]
+    {
+        use crate::android::textinput::input_types::*;
+        let input_type = CLASS_TEXT | TEXT_FLAG_MULTI_LINE | TEXT_FLAG_AUTO_CORRECT;
+
+        let atom = &mut PropertyAtomicGuard::none();
+        node.set_property_u32(atom, Role::App, "android_input_type", input_type).unwrap();
+    }
+
     let mut prop = Property::new("height_range", PropertyType::Float32, PropertySubType::Pixel);
     prop.set_ui_text("Min/Max Height", "Minimum and Maximum height");
     prop.set_range_f32(0., f32::MAX);
@@ -445,6 +465,21 @@ pub fn create_multiline_edit(name: &str) -> SceneNode {
     node
 }
 
+pub fn create_decimal_edit(name: &str) -> SceneNode {
+    let mut node = create_baseedit(name);
+
+    #[cfg(target_os = "android")]
+    {
+        use crate::android::textinput::input_types::*;
+        let input_type = CLASS_NUMBER | NUMBER_FLAG_DECIMAL;
+
+        let atom = &mut PropertyAtomicGuard::none();
+        node.set_property_u32(atom, Role::App, "android_input_type", input_type).unwrap();
+    }
+
+    node
+}
+
 pub fn create_chatview(name: &str) -> SceneNode {
     let mut node = SceneNode::new(name, SceneNodeType::ChatView);
 

+ 4 - 0
bin/app/src/text/editor/android.rs

@@ -231,4 +231,8 @@ impl Editor {
     pub fn buffer(&self) -> String {
         self.state.text.clone()
     }
+
+    pub fn set_input_type(&mut self, input_type: u32) {
+        self.input.set_input_type(input_type);
+    }
 }

+ 13 - 0
bin/app/src/ui/edit/mod.rs

@@ -194,6 +194,8 @@ pub struct BaseEdit {
     priority: PropertyUint32,
     debug: PropertyBool,
 
+    android_input_type: PropertyUint32,
+
     action_fg_color: PropertyColor,
     action_bg_color: PropertyColor,
     action_padding: PropertyFloat32,
@@ -267,6 +269,8 @@ impl BaseEdit {
         let priority = PropertyUint32::wrap(node_ref, Role::Internal, "priority", 0).unwrap();
         let debug = PropertyBool::wrap(node_ref, Role::Internal, "debug", 0).unwrap();
 
+        let android_input_type = PropertyUint32::wrap(node_ref, Role::Internal, "android_input_type", 0).unwrap();
+
         let action_fg_color =
             PropertyColor::wrap(node_ref, Role::Internal, "action_fg_color").unwrap();
         let action_bg_color =
@@ -365,6 +369,8 @@ impl BaseEdit {
             priority,
             debug,
 
+            android_input_type,
+
             action_fg_color,
             action_bg_color,
             action_padding,
@@ -1349,6 +1355,13 @@ impl BaseEdit {
             panic!("self destroyed before insert_text_method_task was stopped!");
         };
 
+        // Set input type from property
+        #[cfg(target_os = "android")]
+        {
+            let input_type = self_.android_input_type.get();
+            self_.editor.lock().set_input_type(input_type);
+        }
+
         self_.editor.lock().focus();
         let atom = &mut self_.renderer.make_guard(gfxtag!("BaseEdit::process_focus_method"));
         self_.is_focused.set(atom, true);