mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-28 14:24:12 +00:00

On android, the handle is the same as the cursor color and the cursor color is usually the "accent" color. We can't know the accent color from the native code, but we know the selection color which is the accent color with a lesser opacity.
314 lines
11 KiB
Java
314 lines
11 KiB
Java
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
|
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.InputConnection;
|
|
import android.content.Context;
|
|
import android.content.res.Configuration;
|
|
import android.graphics.BlendMode;
|
|
import android.graphics.BlendModeColorFilter;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.text.Editable;
|
|
import android.text.Selection;
|
|
import android.text.SpannableStringBuilder;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.app.Activity;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.ImageView;
|
|
import android.widget.PopupWindow;
|
|
import android.view.inputmethod.BaseInputConnection;
|
|
|
|
class InputHandle extends ImageView {
|
|
private PopupWindow mPopupWindow;
|
|
private float mPressedX;
|
|
private float mPressedY;
|
|
private SlintInputView mRootView;
|
|
private int cursorX;
|
|
private int cursorY;
|
|
|
|
public InputHandle(SlintInputView rootView, int attr) {
|
|
super(rootView.getContext());
|
|
mRootView = rootView;
|
|
Context ctx = rootView.getContext();
|
|
// this.mInputView = mInputView;
|
|
|
|
mPopupWindow = new PopupWindow(ctx, null, android.R.attr.textSelectHandleWindowStyle);
|
|
mPopupWindow.setSplitTouchEnabled(true);
|
|
mPopupWindow.setClippingEnabled(false);
|
|
int[] attrs = { attr };
|
|
Drawable drawable = ctx.getTheme().obtainStyledAttributes(attrs).getDrawable(0);
|
|
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);
|
|
}
|
|
|
|
@Override
|
|
public boolean onTouchEvent(MotionEvent ev) {
|
|
switch (ev.getActionMasked()) {
|
|
case MotionEvent.ACTION_DOWN: {
|
|
mPressedX = ev.getRawX() - cursorX;
|
|
mPressedY = ev.getRawY() - cursorY;
|
|
break;
|
|
}
|
|
|
|
case MotionEvent.ACTION_MOVE: {
|
|
SlintAndroidJavaHelper.moveCursorHandle(0, Math.round(ev.getRawX() - mPressedX),
|
|
Math.round(ev.getRawY() - mPressedY));
|
|
break;
|
|
}
|
|
case MotionEvent.ACTION_UP:
|
|
case MotionEvent.ACTION_CANCEL:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void setPosition(int x, int y) {
|
|
cursorX = x;
|
|
cursorY = y;
|
|
|
|
y += mPopupWindow.getHeight();
|
|
x -= mPopupWindow.getWidth() / 2;
|
|
mPopupWindow.showAtLocation(mRootView, 0, x, y);
|
|
mPopupWindow.update(x, y, -1, -1);
|
|
}
|
|
|
|
public void hide() {
|
|
mPopupWindow.dismiss();
|
|
}
|
|
|
|
public void setHandleColor(int color) {
|
|
Drawable drawable = getDrawable();
|
|
if (drawable != null) {
|
|
drawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.SRC_IN));
|
|
setImageDrawable(drawable);
|
|
}
|
|
}
|
|
}
|
|
|
|
class SlintInputView extends View {
|
|
private String mText = "";
|
|
private int mCursorPosition = 0;
|
|
private int mAnchorPosition = 0;
|
|
private int mPreeditStart = 0;
|
|
private int mPreeditEnd = 0;
|
|
private int mInputType = EditorInfo.TYPE_CLASS_TEXT;
|
|
private int mInBatch = 0;
|
|
private boolean mPending = false;
|
|
private SlintEditable mEditable;
|
|
|
|
public class SlintEditable extends SpannableStringBuilder {
|
|
public SlintEditable() {
|
|
super(mText);
|
|
}
|
|
|
|
@Override
|
|
public SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbstart, int tbend) {
|
|
super.replace(start, end, tb, tbstart, tbend);
|
|
mHandle.hide();
|
|
if (mInBatch == 0) {
|
|
update();
|
|
} else {
|
|
mPending = true;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
public SlintInputView(Context context) {
|
|
super(context);
|
|
setFocusable(true);
|
|
setFocusableInTouchMode(true);
|
|
mEditable = new SlintEditable();
|
|
}
|
|
|
|
@Override
|
|
public boolean onCheckIsTextEditor() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
|
outAttrs.inputType = mInputType;
|
|
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
|
outAttrs.initialSelStart = mCursorPosition;
|
|
outAttrs.initialSelEnd = mAnchorPosition;
|
|
return new BaseInputConnection(this, true) {
|
|
@Override
|
|
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, int preeditStart, int preeditEnd,
|
|
int inputType) {
|
|
boolean restart = mInputType != inputType || !mText.equals(text) || mCursorPosition != cursorPosition
|
|
|| mAnchorPosition != anchorPosition;
|
|
mText = text;
|
|
mCursorPosition = cursorPosition;
|
|
mAnchorPosition = anchorPosition;
|
|
mPreeditStart = preeditStart;
|
|
mPreeditEnd = preeditEnd;
|
|
mInputType = inputType;
|
|
|
|
if (restart) {
|
|
mEditable = new SlintEditable();
|
|
Selection.setSelection(mEditable, cursorPosition, anchorPosition);
|
|
InputMethodManager imm = (InputMethodManager) this.getContext()
|
|
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.restartInput(this);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onConfigurationChanged(Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
|
switch (currentNightMode) {
|
|
case Configuration.UI_MODE_NIGHT_NO:
|
|
SlintAndroidJavaHelper.setDarkMode(false);
|
|
break;
|
|
case Configuration.UI_MODE_NIGHT_YES:
|
|
SlintAndroidJavaHelper.setDarkMode(true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private InputHandle mHandle;
|
|
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class SlintAndroidJavaHelper {
|
|
Activity mActivity;
|
|
SlintInputView mInputView;
|
|
|
|
public SlintAndroidJavaHelper(Activity activity) {
|
|
this.mActivity = activity;
|
|
this.mInputView = new SlintInputView(activity);
|
|
this.mActivity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(10, 10);
|
|
mActivity.addContentView(mInputView, params);
|
|
mInputView.setVisibility(View.VISIBLE);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void show_keyboard() {
|
|
mActivity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mInputView.requestFocus();
|
|
InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.showSoftInput(mInputView, 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void hide_keyboard() {
|
|
mActivity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.hideSoftInputFromWindow(mInputView.getWindowToken(), 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
static public native void updateText(String text, int cursorPosition, int anchorPosition, int preeditStart,
|
|
int preeditOffset);
|
|
|
|
static public native void setDarkMode(boolean dark);
|
|
|
|
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) {
|
|
|
|
mActivity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
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);
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
public void set_handle_color(int color) {
|
|
mActivity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mInputView.setHandleColor(color);
|
|
}
|
|
});
|
|
}
|
|
|
|
public boolean dark_color_scheme() {
|
|
int nightModeFlags = mActivity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
|
return nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
|
|
}
|
|
|
|
// Get the geometry of the view minus the system bars and the keyboard
|
|
public Rect get_view_rect() {
|
|
Rect rect = new Rect();
|
|
mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
|
|
return rect;
|
|
}
|
|
}
|