mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-30 15:17:25 +00:00
Android: selection handle
Have two selection handle when there is selected text
This commit is contained in:
parent
21b73769a5
commit
b46effe65c
4 changed files with 128 additions and 38 deletions
|
@ -28,13 +28,13 @@ class InputHandle extends ImageView {
|
|||
private SlintInputView mRootView;
|
||||
private int cursorX;
|
||||
private int cursorY;
|
||||
private int attr;
|
||||
|
||||
public InputHandle(SlintInputView rootView, int attr) {
|
||||
super(rootView.getContext());
|
||||
this.attr = attr;
|
||||
mRootView = rootView;
|
||||
Context ctx = rootView.getContext();
|
||||
// this.mInputView = mInputView;
|
||||
|
||||
mPopupWindow = new PopupWindow(ctx, null, android.R.attr.textSelectHandleWindowStyle);
|
||||
mPopupWindow.setSplitTouchEnabled(true);
|
||||
mPopupWindow.setClippingEnabled(false);
|
||||
|
@ -43,13 +43,6 @@ class InputHandle extends ImageView {
|
|||
mPopupWindow.setWidth(drawable.getIntrinsicWidth());
|
||||
mPopupWindow.setHeight(drawable.getIntrinsicHeight());
|
||||
this.setImageDrawable(drawable);
|
||||
|
||||
// mPopupWindow.setBackgroundDrawable(null);
|
||||
// mPopupWindow.setAnimationStyle(0);
|
||||
// mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
|
||||
// mPopupWindow.setEnterTransition(null);
|
||||
// mPopupWindow.setExitTransition(null);
|
||||
|
||||
mPopupWindow.setContentView(this);
|
||||
}
|
||||
|
||||
|
@ -63,7 +56,9 @@ class InputHandle extends ImageView {
|
|||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
SlintAndroidJavaHelper.moveCursorHandle(0, Math.round(ev.getRawX() - mPressedX),
|
||||
int id = attr == android.R.attr.textSelectHandleLeft ? 1
|
||||
: attr == android.R.attr.textSelectHandleRight ? 2 : 0;
|
||||
SlintAndroidJavaHelper.moveCursorHandle(id, Math.round(ev.getRawX() - mPressedX),
|
||||
Math.round(ev.getRawY() - mPressedY));
|
||||
break;
|
||||
}
|
||||
|
@ -79,7 +74,14 @@ class InputHandle extends ImageView {
|
|||
cursorY = y;
|
||||
|
||||
y += mPopupWindow.getHeight();
|
||||
if (attr == android.R.attr.textSelectHandleLeft) {
|
||||
x -= 3 * mPopupWindow.getWidth() / 4;
|
||||
} else if (attr == android.R.attr.textSelectHandleRight) {
|
||||
x -= mPopupWindow.getWidth() / 4;
|
||||
} else {
|
||||
x -= mPopupWindow.getWidth() / 2;
|
||||
}
|
||||
|
||||
mPopupWindow.showAtLocation(mRootView, 0, x, y);
|
||||
mPopupWindow.update(x, y, -1, -1);
|
||||
}
|
||||
|
@ -116,7 +118,7 @@ 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);
|
||||
mHandle.hide();
|
||||
setCursorPos(0, 0, 0, 0, 0);
|
||||
if (mInBatch == 0) {
|
||||
update();
|
||||
} else {
|
||||
|
@ -211,22 +213,57 @@ class SlintInputView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
private InputHandle mHandle;
|
||||
private InputHandle mCursorHandle;
|
||||
private InputHandle mLeftHandle;
|
||||
private InputHandle mRightHandle;
|
||||
|
||||
public void setCursorPos(int rect_x, int rect_y, int rect_w, int rect_h, boolean show) {
|
||||
if (show) {
|
||||
if (mHandle == null) {
|
||||
mHandle = new InputHandle(this, android.R.attr.textSelectHandle);
|
||||
// num_handles: 0=hidden, 1=cursor handle, 2=selection handles
|
||||
public void setCursorPos(int left_x, int left_y, int right_x, int right_y, int num_handles) {
|
||||
if (num_handles == 1) {
|
||||
if (mLeftHandle != null) {
|
||||
mLeftHandle.hide();
|
||||
}
|
||||
if (mRightHandle != null) {
|
||||
mRightHandle.hide();
|
||||
}
|
||||
if (mCursorHandle == null) {
|
||||
mCursorHandle = new InputHandle(this, android.R.attr.textSelectHandle);
|
||||
}
|
||||
mCursorHandle.setPosition(left_x, left_y);
|
||||
} else if (num_handles == 2) {
|
||||
if (mLeftHandle == null) {
|
||||
mLeftHandle = new InputHandle(this, android.R.attr.textSelectHandleLeft);
|
||||
}
|
||||
if (mRightHandle == null) {
|
||||
mRightHandle = new InputHandle(this, android.R.attr.textSelectHandleRight);
|
||||
}
|
||||
if (mCursorHandle != null) {
|
||||
mCursorHandle.hide();
|
||||
}
|
||||
mLeftHandle.setPosition(right_x, right_y);
|
||||
mRightHandle.setPosition(left_x, left_y);
|
||||
} else {
|
||||
if (mCursorHandle != null) {
|
||||
mCursorHandle.hide();
|
||||
}
|
||||
if (mLeftHandle != null) {
|
||||
mLeftHandle.hide();
|
||||
}
|
||||
if (mRightHandle != null) {
|
||||
mRightHandle.hide();
|
||||
}
|
||||
mHandle.setPosition(rect_x + rect_w / 2, rect_y + rect_h + 2 * rect_w);
|
||||
} else if (mHandle != null) {
|
||||
mHandle.hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void setHandleColor(int color) {
|
||||
if (mHandle != null) {
|
||||
mHandle.setHandleColor(color);
|
||||
if (mCursorHandle != null) {
|
||||
mCursorHandle.setHandleColor(color);
|
||||
}
|
||||
if (mLeftHandle != null) {
|
||||
mLeftHandle.setHandleColor(color);
|
||||
}
|
||||
if (mRightHandle != null) {
|
||||
mRightHandle.setHandleColor(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -277,7 +314,7 @@ public class SlintAndroidJavaHelper {
|
|||
static public native void moveCursorHandle(int id, int pos_x, int pos_y);
|
||||
|
||||
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, boolean show_cursor_handles) {
|
||||
int cur_x, int cur_y, int anchor_x, int anchor_y, int input_type, boolean show_cursor_handles) {
|
||||
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
|
@ -285,7 +322,15 @@ public class SlintAndroidJavaHelper {
|
|||
int selStart = Math.min(cursor_position, anchor_position);
|
||||
int selEnd = Math.max(cursor_position, anchor_position);
|
||||
mInputView.setText(text, selStart, selEnd, preedit_start, preedit_end, input_type);
|
||||
mInputView.setCursorPos(rect_x, rect_y, rect_w, rect_h, show_cursor_handles);
|
||||
int num_handles = 0;
|
||||
if (show_cursor_handles) {
|
||||
num_handles = cursor_position == anchor_position ? 1 : 2;
|
||||
}
|
||||
if (cursor_position < anchor_position) {
|
||||
mInputView.setCursorPos(anchor_x, anchor_y, cur_x, cur_y, num_handles);
|
||||
} else {
|
||||
mInputView.setCursorPos(cur_x, cur_y, anchor_x, anchor_y, num_handles);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -148,9 +148,16 @@ impl JavaHelper {
|
|||
_ => 0 as jint,
|
||||
};
|
||||
|
||||
let cur_origin = data.cursor_rect_origin.to_physical(scale_factor);
|
||||
let cur_origin = dbg!(data.cursor_rect_origin.to_physical(scale_factor));
|
||||
let anchor_origin = dbg!(data.anchor_point.to_physical(scale_factor));
|
||||
let cur_size = data.cursor_rect_size.to_physical(scale_factor);
|
||||
|
||||
// Add 2*cur_size.width to the y position to be a bit under the cursor
|
||||
let cur_x = cur_origin.x + cur_size.width as i32 / 2;
|
||||
let cur_y = cur_origin.y + cur_size.height as i32 + 2 * cur_size.width as i32;
|
||||
let anchor_x = anchor_origin.x;
|
||||
let anchor_y = anchor_origin.y + 2 * cur_size.width as i32;
|
||||
|
||||
env.call_method(
|
||||
helper,
|
||||
"set_imm_data",
|
||||
|
@ -161,10 +168,10 @@ impl JavaHelper {
|
|||
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(cur_origin.x as jint),
|
||||
JValue::from(cur_origin.y as jint),
|
||||
JValue::from(cur_size.width as jint),
|
||||
JValue::from(cur_size.height as jint),
|
||||
JValue::from(cur_x as jint),
|
||||
JValue::from(cur_y as jint),
|
||||
JValue::from(anchor_x as jint),
|
||||
JValue::from(anchor_y as jint),
|
||||
JValue::from(input_type),
|
||||
JValue::from(show_cursor_handles as jboolean),
|
||||
],
|
||||
|
@ -293,7 +300,7 @@ extern "system" fn Java_SlintAndroidJavaHelper_setDarkMode(
|
|||
extern "system" fn Java_SlintAndroidJavaHelper_moveCursorHandle(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
_id: jint,
|
||||
id: jint,
|
||||
pos_x: jint,
|
||||
pos_y: jint,
|
||||
) {
|
||||
|
@ -306,14 +313,44 @@ extern "system" fn Java_SlintAndroidJavaHelper_moveCursorHandle(
|
|||
{
|
||||
if let Some(text_input) = focus_item.downcast::<i_slint_core::items::TextInput>() {
|
||||
let scale_factor = adaptor.window.scale_factor();
|
||||
let pos =
|
||||
euclid::point2(pos_x as f32 / scale_factor, pos_y as f32 / scale_factor)
|
||||
- focus_item.map_to_window(focus_item.geometry().origin).to_vector();
|
||||
let adaptor = adaptor.clone() as Rc<dyn WindowAdapter>;
|
||||
let size = text_input
|
||||
.as_pin_ref()
|
||||
.font_request(&adaptor)
|
||||
.pixel_size
|
||||
.unwrap_or_default()
|
||||
.get();
|
||||
let pos =
|
||||
euclid::point2(
|
||||
pos_x as f32 / scale_factor,
|
||||
pos_y as f32 / scale_factor - size / 2.,
|
||||
) - focus_item.map_to_window(focus_item.geometry().origin).to_vector();
|
||||
let text_pos = text_input.as_pin_ref().byte_offset_for_position(pos, &adaptor);
|
||||
|
||||
let cur_pos = if id == 0 {
|
||||
text_input.anchor_position_byte_offset.set(text_pos as i32);
|
||||
text_pos as i32
|
||||
} else {
|
||||
let current_cursor = text_input.as_pin_ref().cursor_position_byte_offset();
|
||||
let current_anchor = text_input.as_pin_ref().anchor_position_byte_offset();
|
||||
if (id == 1 && current_anchor < current_cursor)
|
||||
|| (id == 2 && current_anchor > current_cursor)
|
||||
{
|
||||
if current_cursor == text_pos as i32 {
|
||||
return;
|
||||
}
|
||||
text_input.anchor_position_byte_offset.set(text_pos as i32);
|
||||
current_cursor
|
||||
} else {
|
||||
if current_anchor == text_pos as i32 {
|
||||
return;
|
||||
}
|
||||
text_pos as i32
|
||||
}
|
||||
};
|
||||
|
||||
text_input.as_pin_ref().set_cursor_position(
|
||||
text_pos as i32,
|
||||
cur_pos,
|
||||
true,
|
||||
i_slint_core::items::TextChangeNotify::TriggerCallbacks,
|
||||
&adaptor,
|
||||
|
|
|
@ -1126,10 +1126,15 @@ impl TextInput {
|
|||
let anchor_position = self.anchor_position(&text);
|
||||
let cursor_relative = self.cursor_rect_for_byte_offset(cursor_position, window_adapter);
|
||||
let geometry = self_rc.geometry();
|
||||
let cursor_rect_origin = crate::api::LogicalPosition::from_euclid(
|
||||
self_rc.map_to_window(cursor_relative.origin + geometry.origin.to_vector()),
|
||||
);
|
||||
let origin = self_rc.map_to_window(geometry.origin).to_vector();
|
||||
let cursor_rect_origin =
|
||||
crate::api::LogicalPosition::from_euclid(cursor_relative.origin + origin);
|
||||
let cursor_rect_size = crate::api::LogicalSize::from_euclid(cursor_relative.size);
|
||||
let anchor_point = crate::api::LogicalPosition::from_euclid(
|
||||
self.cursor_rect_for_byte_offset(anchor_position, window_adapter).origin
|
||||
+ origin
|
||||
+ cursor_relative.size,
|
||||
);
|
||||
|
||||
InputMethodProperties {
|
||||
text,
|
||||
|
@ -1139,6 +1144,7 @@ impl TextInput {
|
|||
preedit_offset: cursor_position,
|
||||
cursor_rect_origin,
|
||||
cursor_rect_size,
|
||||
anchor_point,
|
||||
input_type: self.input_type(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -226,6 +226,8 @@ pub struct InputMethodProperties {
|
|||
pub cursor_rect_origin: LogicalPosition,
|
||||
/// The size of the cursor rectangle.
|
||||
pub cursor_rect_size: crate::api::LogicalSize,
|
||||
/// The position of the anchor (bottom). Only meaningful if anchor_position is Some
|
||||
pub anchor_point: LogicalPosition,
|
||||
/// The type of input for the text edit.
|
||||
pub input_type: InputType,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue