Changes related to text input on Android, general bug fixes, license change to GPLv3
This commit is contained in:
parent
8b3e321ab5
commit
fc8f414b67
308 changed files with 8707 additions and 440 deletions
|
|
@ -1,3 +1,21 @@
|
|||
/*
|
||||
* InfiniPaint
|
||||
* Copyright (C) 2025-2026 Yousef Khadadeh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.erroratline0.infinipaint;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -16,10 +34,6 @@ import org.libsdl.app.SDLActivity;
|
|||
import org.libsdl.app.SDLDummyEdit;
|
||||
import org.libsdl.app.SDLSurface;
|
||||
|
||||
/**
|
||||
* A sample wrapper class that just calls SDLActivity
|
||||
*/
|
||||
|
||||
public class InfiniPaint extends SDLActivity {
|
||||
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
|
@ -40,7 +54,7 @@ public class InfiniPaint extends SDLActivity {
|
|||
}
|
||||
|
||||
static class StartInfiniPaintTextInputTask implements Runnable {
|
||||
public StartInfiniPaintTextInputTask(int textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
|
||||
public StartInfiniPaintTextInputTask(long textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
|
||||
mTextBoxID = textboxID;
|
||||
mStr = str;
|
||||
mSelectionBegin = selectionBegin;
|
||||
|
|
@ -48,7 +62,7 @@ public class InfiniPaint extends SDLActivity {
|
|||
mInputType = inputType;
|
||||
}
|
||||
|
||||
int mTextBoxID;
|
||||
long mTextBoxID;
|
||||
String mStr;
|
||||
int mSelectionBegin;
|
||||
int mSelectionEnd;
|
||||
|
|
@ -57,29 +71,75 @@ public class InfiniPaint extends SDLActivity {
|
|||
@Override
|
||||
public void run() {
|
||||
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(1, 1);
|
||||
//params.leftMargin = 1;
|
||||
//params.topMargin = 1;
|
||||
|
||||
if (mTextEdit == null) {
|
||||
mTextEdit = new InfiniPaintTextBoxView(getContext());
|
||||
mLayout.addView(mTextEdit, params);
|
||||
if (mTextEdit2 == null) {
|
||||
mTextEdit2 = new InfiniPaintTextBoxView(getContext());
|
||||
mLayout.addView(mTextEdit2, params);
|
||||
}
|
||||
else {
|
||||
mTextEdit.setLayoutParams(params);
|
||||
mTextEdit2.setLayoutParams(params);
|
||||
}
|
||||
|
||||
mTextEdit.setInitialData(mTextBoxID, mStr, mSelectionBegin, mSelectionEnd, mInputType);
|
||||
mTextEdit.setVisibility(View.VISIBLE);
|
||||
mTextEdit.requestFocus();
|
||||
mTextEdit2.setInitialData(mTextBoxID, mStr, mSelectionBegin, mSelectionEnd, mInputType);
|
||||
mTextEdit2.setVisibility(View.VISIBLE);
|
||||
mTextEdit2.requestFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(mTextEdit, 0);
|
||||
imm.showSoftInput(mTextEdit2, 0);
|
||||
|
||||
if (imm.isAcceptingText())
|
||||
onNativeScreenKeyboardShown();
|
||||
}
|
||||
}
|
||||
|
||||
static public void startTextInput(int textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
|
||||
mSingleton.commandHandler.post(new StartInfiniPaintTextInputTask(textboxID, str, selectionBegin, selectionEnd, inputType));
|
||||
static class InfiniPaintUpdateCursorPosTask implements Runnable {
|
||||
public InfiniPaintUpdateCursorPosTask(int selectionBegin, int selectionEnd) {
|
||||
mSelectionBegin = selectionBegin;
|
||||
mSelectionEnd = selectionEnd;
|
||||
}
|
||||
|
||||
int mSelectionBegin;
|
||||
int mSelectionEnd;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mTextEdit2 != null) {
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
mTextEdit2.ic.updateSelection(mSelectionBegin, mSelectionEnd);
|
||||
imm.updateSelection(mTextEdit2, mSelectionBegin, mSelectionEnd, -1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static InfiniPaintTextBoxView mTextEdit;
|
||||
static class InfiniPaintUpdateTextboxAndCursorPosTask implements Runnable {
|
||||
public InfiniPaintUpdateTextboxAndCursorPosTask(String str, int selectionBegin, int selectionEnd) {
|
||||
mStr = str;
|
||||
mSelectionBegin = selectionBegin;
|
||||
mSelectionEnd = selectionEnd;
|
||||
}
|
||||
|
||||
String mStr;
|
||||
int mSelectionBegin;
|
||||
int mSelectionEnd;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mTextEdit2 != null) {
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
mTextEdit2.ic.clearAndSetNewTextAndSelection(mStr, mSelectionBegin, mSelectionEnd);
|
||||
imm.updateSelection(mTextEdit2, mSelectionBegin, mSelectionEnd, -1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public void startTextInput(long textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
|
||||
mSingleton.commandHandler.post(new StartInfiniPaintTextInputTask(textboxID, str, selectionBegin, selectionEnd, inputType));
|
||||
}
|
||||
static public void updateCursorPos(int selectionBegin, int selectionEnd) {
|
||||
mSingleton.commandHandler.post(new InfiniPaintUpdateCursorPosTask(selectionBegin, selectionEnd));
|
||||
}
|
||||
static public void updateTextboxAndCursorPos(String str, int selectionBegin, int selectionEnd) {
|
||||
mSingleton.commandHandler.post(new InfiniPaintUpdateTextboxAndCursorPosTask(str, selectionBegin, selectionEnd));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,27 @@
|
|||
/*
|
||||
* InfiniPaint
|
||||
* Copyright (C) 2025-2026 Yousef Khadadeh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.erroratline0.infinipaint;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
|
|
@ -34,7 +53,6 @@ public class InfiniPaintSurface extends SDLSurface {
|
|||
WindowInsets.Type.ime());
|
||||
|
||||
nativeInfiniPaintUpdateIMESafeArea(imeInsets.top, imeInsets.bottom, imeInsets.left, imeInsets.right);
|
||||
|
||||
SDLActivity.onNativeInsetsChanged(combined.left, combined.right, combined.top, combined.bottom);
|
||||
|
||||
if (insets.isVisible(WindowInsets.Type.ime())) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* InfiniPaint
|
||||
* Copyright (C) 2025-2026 Yousef Khadadeh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.erroratline0.infinipaint;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.widget.EditText;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class InfiniPaintTextBoxEditable extends SpannableStringBuilder {
|
||||
public static native void nativeReplace(long mTextBoxID, int st, int en, String str);
|
||||
public static native String nativeGetString(long mTextBoxID);
|
||||
|
||||
public long mTextBoxID;
|
||||
|
||||
InfiniPaintTextBoxEditable() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder replace(int st, int en, CharSequence source, int start, int end) {
|
||||
nativeReplace(mTextBoxID, st, en, source.subSequence(start, end).toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder replace(int st, int en, CharSequence text) {
|
||||
return replace(st, en, text, 0, text.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder insert(int where, CharSequence text, int start, int end) {
|
||||
return replace(where, where, text, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder insert(int where, CharSequence text) {
|
||||
return replace(where, where, text, 0, text.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder delete(int st, int en) {
|
||||
return replace(st, en, "", 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder append(CharSequence text) {
|
||||
return replace(length(), length(), text, 0, text.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder append(CharSequence text, int start, int end) {
|
||||
return replace(length(), length(), text, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder append(char text) {
|
||||
return append(String.valueOf(text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
replace(0, length(), "", 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return nativeGetString(mTextBoxID).length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int i) {
|
||||
return nativeGetString(mTextBoxID).charAt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return nativeGetString(mTextBoxID).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int i, int i1) {
|
||||
return nativeGetString(mTextBoxID).subSequence(i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntStream chars() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
return nativeGetString(mTextBoxID).chars();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntStream codePoints() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
return nativeGetString(mTextBoxID).codePoints();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChars(int i, int i1, char[] chars, int i2) {
|
||||
nativeGetString(mTextBoxID).getChars(i, i1, chars, i2);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,21 @@
|
|||
/*
|
||||
* InfiniPaint
|
||||
* Copyright (C) 2025-2026 Yousef Khadadeh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.erroratline0.infinipaint;
|
||||
|
||||
import static android.text.TextUtils.CAP_MODE_SENTENCES;
|
||||
|
|
@ -9,7 +27,9 @@ import android.os.Bundle;
|
|||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
|
|
@ -40,27 +60,38 @@ import org.libsdl.app.SDL;
|
|||
import org.libsdl.app.SDLActivity;
|
||||
|
||||
public class InfiniPaintTextBoxInputConnection extends BaseInputConnection {
|
||||
public static native void nativeCommitText(int textboxID, String text, int newCursorPosition);
|
||||
public static native void nativeDeleteSurroundingText(int textboxID, int beforeLength, int afterLength);
|
||||
public static native void nativeSetSelection(int textboxID, int start, int end);
|
||||
protected InfiniPaintTextBoxEditable mEditText;
|
||||
protected long mTextBoxID;
|
||||
|
||||
protected EditText mEditText;
|
||||
protected int mTextBoxID;
|
||||
public static native int[] nativeGetSelection(long mTextBoxID);
|
||||
public static native void nativeShiftSelection(long mTextBoxID, int amount);
|
||||
public static native void nativeSetSelection(long mTextBoxID, int start, int end);
|
||||
|
||||
InfiniPaintTextBoxInputConnection(View targetView, boolean fullEditor) {
|
||||
super(targetView, fullEditor);
|
||||
mEditText = new EditText(SDL.getContext());
|
||||
mEditText = new InfiniPaintTextBoxEditable();
|
||||
}
|
||||
|
||||
public void setInitialData(int textboxID, String initialText, int begin, int end) {
|
||||
public void setInitialData(long textboxID, String initialText, int begin, int end) {
|
||||
mTextBoxID = textboxID;
|
||||
mEditText.setText(initialText);
|
||||
mEditText.setSelection(begin, end);
|
||||
mEditText.mTextBoxID = textboxID;
|
||||
Selection.setSelection(mEditText, begin, end);
|
||||
}
|
||||
|
||||
public void updateSelection(int start, int end) {
|
||||
Selection.setSelection(mEditText, start, end);
|
||||
}
|
||||
|
||||
public void clearAndSetNewTextAndSelection(String str, int start, int end) {
|
||||
finishComposingText();
|
||||
mEditText.clear();
|
||||
mEditText.replace(0, 0, str);
|
||||
Selection.setSelection(mEditText, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable getEditable() {
|
||||
return mEditText.getEditableText();
|
||||
return mEditText;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -82,31 +113,147 @@ public class InfiniPaintTextBoxInputConnection extends BaseInputConnection {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
int start = Selection.getSelectionStart(mEditText);
|
||||
int end = Selection.getSelectionEnd(mEditText);
|
||||
if(start != end)
|
||||
commitText("", 0);
|
||||
else {
|
||||
nativeShiftSelection(mTextBoxID, -1);
|
||||
int[] p = nativeGetSelection(mTextBoxID);
|
||||
if(p[0] != start)
|
||||
deleteSurroundingText(start - p[0], 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
leftKey();
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
rightKey();
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_0 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("0", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_1 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("1", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_2 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("2", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_3 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("3", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_4 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("4", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_5 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("5", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_6 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("6", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_7 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("7", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_8 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("8", 1);
|
||||
return true;
|
||||
}
|
||||
else if(event.getKeyCode() == KeyEvent.KEYCODE_9 && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
finishComposingText();
|
||||
commitText("9", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.sendKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
if (!super.commitText(text, newCursorPosition))
|
||||
return false;
|
||||
nativeCommitText(mTextBoxID, text.toString(), newCursorPosition);
|
||||
// Manually do commitText
|
||||
int composingTextBegin = getComposingSpanStart(mEditText);
|
||||
int composingTextEnd = getComposingSpanEnd(mEditText);
|
||||
int start, end;
|
||||
|
||||
if(composingTextBegin != -1 && composingTextEnd != -1) {
|
||||
start = Math.min(composingTextBegin, composingTextEnd);
|
||||
end = Math.max(composingTextBegin, composingTextEnd);
|
||||
}
|
||||
else {
|
||||
start = Math.min(Selection.getSelectionStart(mEditText), Selection.getSelectionEnd(mEditText));
|
||||
end = Math.max(Selection.getSelectionStart(mEditText), Selection.getSelectionEnd(mEditText));
|
||||
}
|
||||
|
||||
mEditText.replace(start, end, text);
|
||||
if(newCursorPosition <= 0)
|
||||
setSelection(start + newCursorPosition, start + newCursorPosition);
|
||||
else
|
||||
setSelection(start + text.length() + newCursorPosition - 1, start + text.length() + newCursorPosition - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
if (!super.setComposingText(text, newCursorPosition))
|
||||
return false;
|
||||
nativeCommitText(mTextBoxID, text.toString(), newCursorPosition);
|
||||
// Manually do setComposingText
|
||||
int composingTextBegin = getComposingSpanStart(mEditText);
|
||||
int composingTextEnd = getComposingSpanEnd(mEditText);
|
||||
int start, end;
|
||||
|
||||
if(composingTextBegin != -1 && composingTextEnd != -1) {
|
||||
start = Math.min(composingTextBegin, composingTextEnd);
|
||||
end = Math.max(composingTextBegin, composingTextEnd);
|
||||
}
|
||||
else {
|
||||
start = Math.min(Selection.getSelectionStart(mEditText), Selection.getSelectionEnd(mEditText));
|
||||
end = Math.max(Selection.getSelectionStart(mEditText), Selection.getSelectionEnd(mEditText));
|
||||
}
|
||||
mEditText.replace(start, end, text);
|
||||
if(newCursorPosition <= 0)
|
||||
setSelection(start + newCursorPosition, start + newCursorPosition);
|
||||
else
|
||||
setSelection(start + text.length() + newCursorPosition - 1, start + text.length() + newCursorPosition - 1);
|
||||
|
||||
setComposingRegion(start, start + text.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
int start = Math.min(Selection.getSelectionStart(mEditText), Selection.getSelectionEnd(mEditText));
|
||||
if (!super.deleteSurroundingText(beforeLength, afterLength))
|
||||
return false;
|
||||
nativeDeleteSurroundingText(mTextBoxID, beforeLength, afterLength);
|
||||
int newCursorPos = Math.max(start - beforeLength, 0);
|
||||
setSelection(newCursorPos, newCursorPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
|
||||
if (!super.deleteSurroundingTextInCodePoints(beforeLength, afterLength))
|
||||
return false;
|
||||
int[] p = nativeGetSelection(mTextBoxID);
|
||||
Selection.setSelection(mEditText, p[0], p[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +262,29 @@ public class InfiniPaintTextBoxInputConnection extends BaseInputConnection {
|
|||
if(!super.setSelection(start, end))
|
||||
return false;
|
||||
nativeSetSelection(mTextBoxID, start, end);
|
||||
int[] p = nativeGetSelection(mTextBoxID);
|
||||
Selection.setSelection(mEditText, p[0], p[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceText(int start, int end, CharSequence text, int newCursorPosition, TextAttribute textAttribute) {
|
||||
finishComposingText();
|
||||
setSelection(start, end);
|
||||
commitText(text, newCursorPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void leftKey() {
|
||||
nativeShiftSelection(mTextBoxID, -1);
|
||||
int[] p = nativeGetSelection(mTextBoxID);
|
||||
Selection.setSelection(mEditText, p[0], p[1]);
|
||||
}
|
||||
|
||||
public void rightKey() {
|
||||
nativeShiftSelection(mTextBoxID, 1);
|
||||
int[] p = nativeGetSelection(mTextBoxID);
|
||||
Selection.setSelection(mEditText, p[0], p[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,38 @@
|
|||
/*
|
||||
* InfiniPaint
|
||||
* Copyright (C) 2025-2026 Yousef Khadadeh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.erroratline0.infinipaint;
|
||||
|
||||
import android.content.*;
|
||||
import android.text.InputType;
|
||||
import android.text.Selection;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
public class InfiniPaintTextBoxView extends View
|
||||
import org.libsdl.app.SDLActivity;
|
||||
|
||||
public class InfiniPaintTextBoxView extends View implements View.OnKeyListener
|
||||
{
|
||||
public InfiniPaintTextBoxInputConnection ic;
|
||||
|
||||
int mTextBoxID;
|
||||
long mTextBoxID;
|
||||
String mStr;
|
||||
int mSelectionBegin;
|
||||
int mSelectionEnd;
|
||||
|
|
@ -20,9 +42,10 @@ public class InfiniPaintTextBoxView extends View
|
|||
super(context);
|
||||
setFocusableInTouchMode(true);
|
||||
setFocusable(true);
|
||||
setOnKeyListener(this);
|
||||
}
|
||||
|
||||
void setInitialData(int textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
|
||||
void setInitialData(long textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
|
||||
mTextBoxID = textboxID;
|
||||
mStr = str;
|
||||
mSelectionBegin = selectionBegin;
|
||||
|
|
@ -35,6 +58,23 @@ public class InfiniPaintTextBoxView extends View
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyPreIme (int keyCode, KeyEvent event) {
|
||||
// As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
|
||||
// FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
|
||||
// FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
|
||||
// FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
|
||||
// FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
|
||||
// FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
|
||||
if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (SDLActivity.mTextEdit2 != null && SDLActivity.mTextEdit2.getVisibility() == View.VISIBLE) {
|
||||
SDLActivity.onNativeKeyboardFocusLost();
|
||||
}
|
||||
}
|
||||
|
||||
return super.onKeyPreIme(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
ic = new InfiniPaintTextBoxInputConnection(this, true);
|
||||
|
|
@ -42,10 +82,14 @@ public class InfiniPaintTextBoxView extends View
|
|||
|
||||
outAttrs.inputType = mInputType;
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */ |
|
||||
EditorInfo.IME_ACTION_DONE;
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
|
||||
|
||||
return ic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
package com.erroratline0.infinipaint;
|
||||
|
||||
public class IntPair {
|
||||
IntPair() {
|
||||
first = 0;
|
||||
second = 0;
|
||||
}
|
||||
IntPair(int initFirst, int initSecond) {
|
||||
first = initFirst;
|
||||
second = initSecond;
|
||||
}
|
||||
public int first;
|
||||
public int second;
|
||||
}
|
||||
|
|
@ -48,6 +48,8 @@ import android.widget.RelativeLayout;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.erroratline0.infinipaint.InfiniPaintTextBoxView;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
|
|
@ -57,7 +59,7 @@ import java.util.Locale;
|
|||
/**
|
||||
SDL Activity
|
||||
*/
|
||||
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
|
||||
public abstract class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
|
||||
private static final String TAG = "SDL";
|
||||
private static final int SDL_MAJOR_VERSION = 3;
|
||||
private static final int SDL_MINOR_VERSION = 4;
|
||||
|
|
@ -215,6 +217,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
protected static SDLActivity mSingleton;
|
||||
protected static SDLSurface mSurface;
|
||||
protected static SDLDummyEdit mTextEdit;
|
||||
public static InfiniPaintTextBoxView mTextEdit2;
|
||||
protected static ViewGroup mLayout;
|
||||
protected static SDLClipboardHandler mClipboardHandler;
|
||||
protected static Hashtable<Integer, PointerIcon> mCursors;
|
||||
|
|
@ -326,6 +329,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
mSingleton = null;
|
||||
mSurface = null;
|
||||
mTextEdit = null;
|
||||
mTextEdit2 = null;
|
||||
mLayout = null;
|
||||
mClipboardHandler = null;
|
||||
mCursors = new Hashtable<Integer, PointerIcon>();
|
||||
|
|
@ -943,14 +947,14 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
}
|
||||
break;
|
||||
case COMMAND_TEXTEDIT_HIDE:
|
||||
if (mTextEdit != null) {
|
||||
if (mTextEdit2 != null) {
|
||||
// Note: On some devices setting view to GONE creates a flicker in landscape.
|
||||
// Setting the View's sizes to 0 is similar to GONE but without the flicker.
|
||||
// The sizes will be set to useful values when the keyboard is shown again.
|
||||
mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
|
||||
mTextEdit2.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
|
||||
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
|
||||
imm.hideSoftInputFromWindow(mTextEdit2.getWindowToken(), 0);
|
||||
|
||||
onNativeScreenKeyboardHidden();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue