mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-30 15:17:25 +00:00
Android: support for preedit
(Tested with the typewise keyboard)
This commit is contained in:
parent
1fb162936e
commit
c4e0f4a265
2 changed files with 85 additions and 42 deletions
|
@ -19,14 +19,14 @@ class SlintInputView extends View {
|
|||
private String mText = "";
|
||||
private int mCursorPosition = 0;
|
||||
private int mAnchorPosition = 0;
|
||||
private String mPreedit = "";
|
||||
private int mPreeditOffset;
|
||||
private int mPreeditStart = 0;
|
||||
private int mPreeditEnd = 0;
|
||||
private int mInputType = EditorInfo.TYPE_CLASS_TEXT;
|
||||
private SpannableStringBuilder mEditable;
|
||||
private int mInBatch = 0;
|
||||
private boolean mPending = false;
|
||||
private SlintEditable mEditable;
|
||||
|
||||
public class SlintEditable extends SpannableStringBuilder {
|
||||
// private SlintInputView mInputView;
|
||||
|
||||
public SlintEditable() {
|
||||
super(mText);
|
||||
}
|
||||
|
@ -34,18 +34,23 @@ class SlintInputView extends View {
|
|||
@Override
|
||||
public SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbstart, int tbend) {
|
||||
super.replace(start, end, tb, tbstart, tbend);
|
||||
mText = toString();
|
||||
mCursorPosition = Selection.getSelectionStart(this);
|
||||
mAnchorPosition = Selection.getSelectionEnd(this);
|
||||
SlintAndroidJavaHelper.updateText(mText, mCursorPosition, mAnchorPosition, "", mCursorPosition);
|
||||
System.out.println("replace '" + toString() + "' mInBatch=" + mInBatch);
|
||||
if (mInBatch == 0) {
|
||||
update();
|
||||
} else {
|
||||
mPending = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpan(Object what, int start, int end, int flags) {
|
||||
super.setSpan(what, start, end, flags);
|
||||
public void update() {
|
||||
mPending = false;
|
||||
mText = toString();
|
||||
mCursorPosition = Selection.getSelectionStart(this);
|
||||
mAnchorPosition = Selection.getSelectionEnd(this);
|
||||
mPreeditStart = BaseInputConnection.getComposingSpanStart(this);
|
||||
mPreeditEnd = BaseInputConnection.getComposingSpanEnd(this);
|
||||
SlintAndroidJavaHelper.updateText(mText, mCursorPosition, mAnchorPosition, mPreeditStart, mPreeditEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,18 +77,33 @@ class SlintInputView extends View {
|
|||
public Editable getEditable() {
|
||||
return mEditable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beginBatchEdit() {
|
||||
mInBatch += 1;
|
||||
return super.beginBatchEdit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean endBatchEdit() {
|
||||
mInBatch -= 1;
|
||||
if (mInBatch == 0 && mPending) {
|
||||
mEditable.update();
|
||||
}
|
||||
return super.endBatchEdit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void setText(String text, int cursorPosition, int anchorPosition, String preedit, int preeditOffset,
|
||||
public void setText(String text, int cursorPosition, int anchorPosition, int preeditStart, int preeditEnd,
|
||||
int inputType) {
|
||||
boolean restart = mInputType != inputType || !mText.equals(text) || mCursorPosition != cursorPosition
|
||||
|| mAnchorPosition != anchorPosition;
|
||||
mText = text;
|
||||
mCursorPosition = cursorPosition;
|
||||
mAnchorPosition = anchorPosition;
|
||||
mPreedit = preedit;
|
||||
mPreeditOffset = preeditOffset;
|
||||
mPreeditStart = preeditStart;
|
||||
mPreeditEnd = preeditEnd;
|
||||
mInputType = inputType;
|
||||
|
||||
if (restart) {
|
||||
|
@ -148,12 +168,12 @@ public class SlintAndroidJavaHelper {
|
|||
});
|
||||
}
|
||||
|
||||
static public native void updateText(String text, int cursorPosition, int anchorPosition, String preedit,
|
||||
static public native void updateText(String text, int cursorPosition, int anchorPosition, int preeditStart,
|
||||
int preeditOffset);
|
||||
|
||||
static public native void setDarkMode(boolean dark);
|
||||
|
||||
public void set_imm_data(String text, int cursor_position, int anchor_position, String preedit, int preedit_offset,
|
||||
public void set_imm_data(String text, int cursor_position, int anchor_position, int preedit_start, int preedit_end,
|
||||
int rect_x, int rect_y, int rect_w, int rect_h, int input_type) {
|
||||
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
|
@ -164,7 +184,7 @@ public class SlintAndroidJavaHelper {
|
|||
mInputView.setLayoutParams(layoutParams);
|
||||
int selStart = Math.min(cursor_position, anchor_position);
|
||||
int selEnd = Math.max(cursor_position, anchor_position);
|
||||
mInputView.setText(text, selStart, selEnd, preedit, preedit_offset, input_type);
|
||||
mInputView.setText(text, selStart, selEnd, preedit_start, preedit_end, input_type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ fn load_java_helper(app: &AndroidApp) -> Result<jni::objects::GlobalRef, jni::er
|
|||
let methods = [
|
||||
jni::NativeMethod {
|
||||
name: "updateText".into(),
|
||||
sig: "(Ljava/lang/String;IILjava/lang/String;I)V".into(),
|
||||
sig: "(Ljava/lang/String;IIII)V".into(),
|
||||
fn_ptr: Java_SlintAndroidJavaHelper_updateText as *mut _,
|
||||
},
|
||||
jni::NativeMethod {
|
||||
|
@ -105,9 +105,22 @@ impl JavaHelper {
|
|||
data: &i_slint_core::window::InputMethodProperties,
|
||||
) -> Result<(), jni::errors::Error> {
|
||||
self.with_jni_env(|env, helper| {
|
||||
let text = &env.new_string(data.text.as_str())?;
|
||||
let preedit_text = env.new_string(data.preedit_text.as_str())?;
|
||||
let to_utf16 = |x| convert_utf8_index_to_utf16(&data.text, x as usize);
|
||||
let mut text = data.text.to_string();
|
||||
let mut cursor_position = data.cursor_position;
|
||||
let mut anchor_position = data.anchor_position.unwrap_or(data.cursor_position);
|
||||
|
||||
if !data.preedit_text.is_empty() {
|
||||
text.insert_str(data.preedit_offset, data.preedit_text.as_str());
|
||||
if cursor_position >= data.preedit_offset {
|
||||
cursor_position += data.preedit_text.len()
|
||||
}
|
||||
if anchor_position >= data.preedit_offset {
|
||||
anchor_position += data.preedit_text.len()
|
||||
}
|
||||
}
|
||||
|
||||
let to_utf16 = |x| convert_utf8_index_to_utf16(&text, x as usize);
|
||||
let text = &env.new_string(text.as_str())?;
|
||||
|
||||
let class_it = env.find_class("android/text/InputType")?;
|
||||
let input_type = match data.input_type {
|
||||
|
@ -128,15 +141,13 @@ impl JavaHelper {
|
|||
env.call_method(
|
||||
helper,
|
||||
"set_imm_data",
|
||||
"(Ljava/lang/String;IILjava/lang/String;IIIIII)V",
|
||||
"(Ljava/lang/String;IIIIIIIII)V",
|
||||
&[
|
||||
JValue::Object(&text),
|
||||
JValue::from(to_utf16(data.cursor_position) as jint),
|
||||
JValue::from(
|
||||
to_utf16(data.anchor_position.unwrap_or(data.cursor_position)) as jint
|
||||
),
|
||||
JValue::Object(&preedit_text),
|
||||
JValue::from(to_utf16(cursor_position) as jint),
|
||||
JValue::from(to_utf16(anchor_position) as jint),
|
||||
JValue::from(to_utf16(data.preedit_offset) as jint),
|
||||
JValue::from(to_utf16(data.preedit_offset + data.preedit_text.len()) as jint),
|
||||
JValue::from(data.cursor_rect_origin.x as jint),
|
||||
JValue::from(data.cursor_rect_origin.y as jint),
|
||||
JValue::from(data.cursor_rect_size.width as jint),
|
||||
|
@ -175,8 +186,8 @@ extern "system" fn Java_SlintAndroidJavaHelper_updateText(
|
|||
text: JString,
|
||||
cursor_position: jint,
|
||||
anchor_position: jint,
|
||||
preedit: JString,
|
||||
preedit_offset: jint,
|
||||
preedit_start: jint,
|
||||
preedit_end: jint,
|
||||
) {
|
||||
fn make_shared_string(env: &mut JNIEnv, string: &JString) -> Option<SharedString> {
|
||||
let java_str = env.get_string(&string).ok()?;
|
||||
|
@ -184,24 +195,36 @@ extern "system" fn Java_SlintAndroidJavaHelper_updateText(
|
|||
Some(SharedString::from(decoded.as_ref()))
|
||||
}
|
||||
let Some(text) = make_shared_string(&mut env, &text) else { return };
|
||||
let Some(preedit) = make_shared_string(&mut env, &preedit) else { return };
|
||||
|
||||
let cursor_position = convert_utf16_index_to_utf8(&text, cursor_position as usize);
|
||||
let anchor_position = convert_utf16_index_to_utf8(&text, anchor_position as usize);
|
||||
let preedit_offset = convert_utf16_index_to_utf8(&text, preedit_offset as usize) as i32;
|
||||
let preedit_start = convert_utf16_index_to_utf8(&text, preedit_start as usize);
|
||||
let preedit_end = convert_utf16_index_to_utf8(&text, preedit_end as usize);
|
||||
|
||||
i_slint_core::api::invoke_from_event_loop(move || {
|
||||
if let Some(adaptor) = CURRENT_WINDOW.with_borrow(|x| x.upgrade()) {
|
||||
let runtime_window = i_slint_core::window::WindowInner::from_pub(&adaptor.window);
|
||||
let event = i_slint_core::input::KeyEvent {
|
||||
let event = if preedit_start != preedit_end {
|
||||
let adjust = |pos| if pos <= preedit_start { pos } else if pos >= preedit_end { pos - preedit_end + preedit_start } else { preedit_start } as i32;
|
||||
i_slint_core::input::KeyEvent {
|
||||
event_type: i_slint_core::input::KeyEventType::UpdateComposition,
|
||||
text: i_slint_core::format!( "{}{}", &text[..preedit_start], &text[preedit_end..]),
|
||||
preedit_text: text[preedit_start..preedit_end].into(),
|
||||
preedit_selection: Some(0..(preedit_end - preedit_start) as i32),
|
||||
replacement_range: Some(i32::MIN..i32::MAX),
|
||||
cursor_position: Some(adjust(cursor_position)),
|
||||
anchor_position: Some(adjust(anchor_position)),
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
i_slint_core::input::KeyEvent {
|
||||
event_type: i_slint_core::input::KeyEventType::CommitComposition,
|
||||
text,
|
||||
replacement_range: Some(i32::MIN..i32::MAX),
|
||||
cursor_position: Some(cursor_position as _),
|
||||
anchor_position: Some(anchor_position as _),
|
||||
preedit_selection: (!preedit.is_empty())
|
||||
.then(|| preedit_offset..(preedit_offset + preedit.len() as i32)),
|
||||
preedit_text: preedit,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
runtime_window.process_key_input(event);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue