Rewrote how input text box focus is decided

This commit is contained in:
Yousef Khadadeh 2026-05-07 21:48:47 +03:00
parent 1585e0aa67
commit 8b3e321ab5
41 changed files with 317 additions and 490 deletions

View file

@ -17,7 +17,7 @@
#}
-keep,includedescriptorclasses,allowoptimization class com.erroratline0.infinipaint.InfiniPaint {
void startTextInput();
void startTextInput(int);
}
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {

View file

@ -40,7 +40,19 @@ public class InfiniPaint extends SDLActivity {
}
static class StartInfiniPaintTextInputTask implements Runnable {
public StartInfiniPaintTextInputTask() {}
public StartInfiniPaintTextInputTask(int textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
mTextBoxID = textboxID;
mStr = str;
mSelectionBegin = selectionBegin;
mSelectionEnd = selectionEnd;
mInputType = inputType;
}
int mTextBoxID;
String mStr;
int mSelectionBegin;
int mSelectionEnd;
int mInputType;
@Override
public void run() {
@ -50,9 +62,11 @@ public class InfiniPaint extends SDLActivity {
mTextEdit = new InfiniPaintTextBoxView(getContext());
mLayout.addView(mTextEdit, params);
}
else
else {
mTextEdit.setLayoutParams(params);
}
mTextEdit.setInitialData(mTextBoxID, mStr, mSelectionBegin, mSelectionEnd, mInputType);
mTextEdit.setVisibility(View.VISIBLE);
mTextEdit.requestFocus();
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@ -63,8 +77,8 @@ public class InfiniPaint extends SDLActivity {
}
}
static public void startTextInput() {
mSingleton.commandHandler.post(new StartInfiniPaintTextInputTask());
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 InfiniPaintTextBoxView mTextEdit;

View file

@ -9,6 +9,7 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.view.*;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
@ -38,16 +39,25 @@ import android.widget.EditText;
import org.libsdl.app.SDL;
import org.libsdl.app.SDLActivity;
class InfiniPaintTextBoxInputConnection extends BaseInputConnection
{
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 EditText mEditText;
protected String mCommittedText = "";
protected int mTextBoxID;
InfiniPaintTextBoxInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
mEditText = new EditText(SDL.getContext());
}
public void setInitialData(int textboxID, String initialText, int begin, int end) {
mTextBoxID = textboxID;
mEditText.setText(initialText);
mEditText.setSelection(begin, end);
}
@Override
public Editable getEditable() {
return mEditText.getEditableText();
@ -78,253 +88,34 @@ class InfiniPaintTextBoxInputConnection extends BaseInputConnection
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
if (!super.commitText(text, newCursorPosition)) {
if (!super.commitText(text, newCursorPosition))
return false;
}
updateText();
nativeCommitText(mTextBoxID, text.toString(), newCursorPosition);
return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (!super.setComposingText(text, newCursorPosition)) {
if (!super.setComposingText(text, newCursorPosition))
return false;
}
updateText();
nativeCommitText(mTextBoxID, text.toString(), newCursorPosition);
return true;
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if (beforeLength > 0 && afterLength == 0) {
// backspace(s)
while (beforeLength-- > 0) {
}
return true;
}
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
if (!super.deleteSurroundingText(beforeLength, afterLength))
return false;
}
updateText();
nativeDeleteSurroundingText(mTextBoxID, beforeLength, afterLength);
return true;
}
protected void updateText() {
final Editable content = getEditable();
if (content == null) {
return;
}
String text = content.toString();
int compareLength = Math.min(text.length(), mCommittedText.length());
int matchLength, offset;
/* Backspace over characters that are no longer in the string */
for (matchLength = 0; matchLength < compareLength; ) {
int codePoint = mCommittedText.codePointAt(matchLength);
if (codePoint != text.codePointAt(matchLength)) {
break;
}
matchLength += Character.charCount(codePoint);
}
/* FIXME: This doesn't handle graphemes, like '🌬️' */
for (offset = matchLength; offset < mCommittedText.length(); ) {
int codePoint = mCommittedText.codePointAt(offset);
offset += Character.charCount(codePoint);
}
if (matchLength < text.length()) {
String pendingText = text.subSequence(matchLength, text.length()).toString();
if (!SDLActivity.dispatchingKeyEvent()) {
for (offset = 0; offset < pendingText.length(); ) {
int codePoint = pendingText.codePointAt(offset);
if (codePoint == '\n') {
if (SDLActivity.onNativeSoftReturnKey()) {
return;
}
}
/* Higher code points don't generate simulated scancodes */
if (codePoint > 0 && codePoint < 128) {
}
offset += Character.charCount(codePoint);
}
}
}
mCommittedText = text;
@Override
public boolean setSelection(int start, int end) {
if(!super.setSelection(start, end))
return false;
nativeSetSelection(mTextBoxID, start, end);
return true;
}
}
//class InfiniPaintTextBoxInputConnection extends BaseInputConnection {
// InfiniPaintTextBoxInputConnection(View targetView, boolean fullEditor) {
// super(targetView, fullEditor);
// }
//
// public native void nativeBeginBatchEdit();
// public native void nativeClearMetaKeyStates(int states);
// public native void nativeSetComposingText(CharSequence text, int newCursorPosition);
// public native void nativeSetComposingRegion(int start, int end);
// public native void nativeSetSelection(int start, int end);
// public native void nativeCommitCorrection(int offset, CharSequence oldText, CharSequence newText);
// public native void nativeCommitText(CharSequence text, int newCursorPosition);
// public native void nativeDeleteSurroundingText(int beforeLength, int afterLength);
// public native void nativeDeleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
// public native void nativeEndBatchEdit();
// public native void nativeFinishComposingText();
// public native CharSequence nativeGetSelectedText();
// public native CharSequence nativeGetTextAfterCursor(int n);
// public native CharSequence nativeGetTextBeforeCursor(int n);
// public native void nativePerformContextMenuAction(int action);
// public native void nativePerformEditorAction(int action);
//
// @Override
// public boolean beginBatchEdit() {
// nativeBeginBatchEdit();
// return true;
// }
//
// @Override
// public boolean clearMetaKeyStates(int states) {
// nativeClearMetaKeyStates(states);
// return true;
// }
//
// @Override
// public boolean performSpellCheck() {
// return true;
// }
//
// @Override
// public TextSnapshot takeSnapshot() {
// return null;
// }
//
// @Override
// public CharSequence getTextBeforeCursor(int n, int flags) {
// return nativeGetTextBeforeCursor(n);
// }
//
// @Override
// public CharSequence getTextAfterCursor(int n, int flags) {
// return nativeGetTextAfterCursor(n);
// }
//
// @Override
// public CharSequence getSelectedText(int i) {
// return nativeGetSelectedText();
// }
//
// @Override
// public int getCursorCapsMode(int i) {
// return CAP_MODE_SENTENCES;
// }
//
// @Override
// public ExtractedText getExtractedText(ExtractedTextRequest extractedTextRequest, int i) {
// return null;
// }
//
// @Override
// public boolean deleteSurroundingText(int i, int i1) {
// nativeDeleteSurroundingText(i, i1);
// return true;
// }
//
// @Override
// public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
// nativeDeleteSurroundingTextInCodePoints(beforeLength, afterLength);
// return true;
// }
//
// @Override
// public boolean setComposingRegion(int i, int i1) {
// nativeSetComposingRegion(i, i1);
// return true;
// }
//
// @Override
// public boolean finishComposingText() {
// nativeFinishComposingText();
// return true;
// }
//
// @Override
// public boolean commitCompletion(CompletionInfo completionInfo) {
// return true;
// }
//
// @Override
// public boolean commitCorrection(CorrectionInfo correctionInfo) {
// nativeCommitCorrection(correctionInfo.getOffset(), correctionInfo.getOldText(), correctionInfo.getNewText());
// return true;
// }
//
// @Override
// public boolean setSelection(int i, int i1) {
// nativeSetSelection(i, i1);
// return true;
// }
//
// @Override
// public boolean performEditorAction(int i) {
// nativePerformEditorAction(i);
// return true;
// }
//
// @Override
// public boolean performContextMenuAction(int i) {
// nativePerformContextMenuAction(i);
// return true;
// }
//
// @Override
// public boolean endBatchEdit() {
// nativeEndBatchEdit();
// return true;
// }
//
// @Override
// public boolean sendKeyEvent(KeyEvent keyEvent) {
// return true;
// }
//
// @Override
// public boolean reportFullscreenMode(boolean b) {
// return true;
// }
//
// @Override
// public boolean performPrivateCommand(String s, Bundle bundle) {
// return false;
// }
//
// @Override
// public boolean requestCursorUpdates(int i) {
// return true;
// }
//
// @Override
// public Handler getHandler() {
// return null;
// }
//
// @Override
// public boolean commitContent(InputContentInfo inputContentInfo, int i, Bundle bundle) {
// return true;
// }
//
// @Override
// public boolean commitText(CharSequence text, int newCursorPosition) {
// nativeCommitText(text, newCursorPosition);
// return true;
// }
//
// @Override
// public boolean setComposingText(CharSequence text, int newCursorPosition) {
// nativeSetComposingText(text, newCursorPosition);
// return true;
// }
//}

View file

@ -8,7 +8,13 @@ import android.view.inputmethod.InputConnection;
public class InfiniPaintTextBoxView extends View
{
InputConnection ic;
public InfiniPaintTextBoxInputConnection ic;
int mTextBoxID;
String mStr;
int mSelectionBegin;
int mSelectionEnd;
int mInputType;
InfiniPaintTextBoxView(Context context) {
super(context);
@ -16,6 +22,14 @@ public class InfiniPaintTextBoxView extends View
setFocusable(true);
}
void setInitialData(int textboxID, String str, int selectionBegin, int selectionEnd, int inputType) {
mTextBoxID = textboxID;
mStr = str;
mSelectionBegin = selectionBegin;
mSelectionEnd = selectionEnd;
mInputType = inputType;
}
@Override
public boolean onCheckIsTextEditor() {
return true;
@ -24,8 +38,9 @@ public class InfiniPaintTextBoxView extends View
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ic = new InfiniPaintTextBoxInputConnection(this, true);
ic.setInitialData(mTextBoxID, mStr, mSelectionBegin, mSelectionEnd);
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
outAttrs.inputType = mInputType;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */ |
EditorInfo.IME_ACTION_DONE;

View file

@ -0,0 +1,14 @@
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;
}

View file

@ -11,15 +11,15 @@ namespace AndroidJNICalls {
InputManager* globalInputManager;
std::mutex globalCallMutex;
void startTextInput() {
void startTextInput(int input_type) {
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
Logger::get().log("INFO", std::to_string((uint64_t)env) + " jni env value");
jobject activity = (jobject)SDL_GetAndroidActivity();
Logger::get().log("INFO", std::to_string((uint64_t)activity) + " activity value");
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "startTextInput", "()V");
jmethodID method_id = env->GetStaticMethodID(clazz, "startTextInput", "(I)V");
Logger::get().log("INFO", std::to_string((uint64_t)method_id) + " method value");
env->CallStaticVoidMethod(clazz, method_id);
env->CallStaticVoidMethod(clazz, method_id, input_type);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
}
@ -35,6 +35,27 @@ JNIEnv* jniEnv;
using namespace AndroidJNICalls;
// https://stackoverflow.com/questions/41820039/jstringjni-to-stdstringc-with-utf8-characters
std::string jstring2string(JNIEnv *env, jstring jStr) {
if (!jStr)
return "";
const jclass stringClass = env->GetObjectClass(jStr);
const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jStr, getBytes, env->NewStringUTF("UTF-8"));
size_t length = (size_t) env->GetArrayLength(stringJbytes);
jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);
std::string ret = std::string((char *)pBytes, length);
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
env->DeleteLocalRef(stringJbytes);
env->DeleteLocalRef(stringClass);
return ret;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintSurface_nativeInfiniPaintUpdateIMESafeArea(JNIEnv *env, jclass clazz, jint top, jint bottom, jint left, jint right) {
@ -46,103 +67,12 @@ Java_com_erroratline0_infinipaint_InfiniPaintSurface_nativeInfiniPaintUpdateIMES
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeBeginBatchEdit(
JNIEnv *env, jobject jobject1) {
// TODO: implement nativeBeginBatchEdit()
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetTextBoxCursor(
JNIEnv *env, jclass clazz, jint cursor_begin, jint cursor_end) {
if(globalInputManager) {
std::scoped_lock a{globalCallMutex};
globalInputManager->main.input_android_text_box_set_cursor(cursor_begin, cursor_end);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeClearMetaKeyStates(
JNIEnv *env, jobject jobject1, jint states) {
// TODO: implement nativeClearMetaKeyStates()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetComposingText(
JNIEnv *env, jobject jobject1, jobject text, jint new_cursor_position) {
// TODO: implement nativeSetComposingText()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetComposingRegion(
JNIEnv *env, jobject jobject1, jint start, jint end) {
// TODO: implement nativeSetComposingRegion()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetSelection(JNIEnv *env,
jobject jobject1,
jint start,
jint end) {
// TODO: implement nativeSetSelection()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeCommitCorrection(
JNIEnv *env, jobject jobject1, jint offset, jobject old_text, jobject new_text) {
// TODO: implement nativeCommitCorrection()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeCommitText(JNIEnv *env,
jobject jobject1,
jobject text,
jint new_cursor_position) {
// TODO: implement nativeCommitText()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeDeleteSurroundingText(
JNIEnv *env, jobject jobject1, jint before_length, jint after_length) {
// TODO: implement nativeDeleteSurroundingText()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeDeleteSurroundingTextInCodePoints(
JNIEnv *env, jobject jobject1, jint before_length, jint after_length) {
// TODO: implement nativeDeleteSurroundingTextInCodePoints()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeEndBatchEdit(JNIEnv *env,
jobject jobject1) {
// TODO: implement nativeEndBatchEdit()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeFinishComposingText(
JNIEnv *env, jobject jobject1) {
// TODO: implement nativeFinishComposingText()
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeGetSelectedText(
JNIEnv *env, jobject jobject1) {
// TODO: implement nativeGetSelectedText()
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeGetTextAfterCursor(
JNIEnv *env, jobject jobject1, jint n) {
// TODO: implement nativeGetTextAfterCursor()
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeGetTextBeforeCursor(
JNIEnv *env, jobject jobject1, jint n) {
// TODO: implement nativeGetTextBeforeCursor()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativePerformContextMenuAction(
JNIEnv *env, jobject jobject1, jint action) {
// TODO: implement nativePerformContextMenuAction()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativePerformEditorAction(
JNIEnv *env, jobject jobject1, jint action) {
// TODO: implement nativePerformEditorAction()
}
#endif

View file

@ -8,7 +8,7 @@ namespace AndroidJNICalls {
extern InputManager* globalInputManager;
extern std::mutex globalCallMutex;
void startTextInput();
void startTextInput(int input_type);
}
#endif

View file

@ -15,6 +15,7 @@ void init() {
PasteEvent::EVENT_NUM = SDL_RegisterEvents(1);
OpenInfiniPaintFileEvent::EVENT_NUM = SDL_RegisterEvents(1);
AddFileToCanvasEvent::EVENT_NUM = SDL_RegisterEvents(1);
RefreshTextBoxInputEvent::EVENT_NUM = SDL_RegisterEvents(1);
alreadyInitialized = true;
}
}
@ -22,5 +23,6 @@ void init() {
uint32_t PasteEvent::EVENT_NUM;
uint32_t OpenInfiniPaintFileEvent::EVENT_NUM;
uint32_t AddFileToCanvasEvent::EVENT_NUM;
uint32_t RefreshTextBoxInputEvent::EVENT_NUM;
}

View file

@ -67,4 +67,8 @@ namespace CustomEvents {
std::filesystem::path filePath;
Vector2f pos;
};
struct RefreshTextBoxInputEvent {
static uint32_t EVENT_NUM;
};
}

View file

@ -114,7 +114,7 @@ void DrawCamera::update_main(World& w) {
checks_after_input(w);
}
else {
if(w.main.input.key(InputManager::KEY_CAMERA_ROTATE_COUNTERCLOCKWISE).held && !w.main.input.text.is_accepting_input()) {
if(w.main.input.key(InputManager::KEY_CAMERA_ROTATE_COUNTERCLOCKWISE).held && !w.main.input.text_is_accepting_input()) {
c.rotate_about(c.from_space(w.main.window.size.cast<float>() * 0.5f), -w.main.deltaTime);
InputManager::MouseMotionCallbackArgs motion{
.pos = w.main.input.mouse.pos,
@ -124,7 +124,7 @@ void DrawCamera::update_main(World& w) {
w.main.g.gui.set_to_layout();
checks_after_input(w);
}
if(w.main.input.key(InputManager::KEY_CAMERA_ROTATE_CLOCKWISE).held && !w.main.input.text.is_accepting_input()) {
if(w.main.input.key(InputManager::KEY_CAMERA_ROTATE_CLOCKWISE).held && !w.main.input.text_is_accepting_input()) {
c.rotate_about(c.from_space(w.main.window.size.cast<float>() * 0.5f), w.main.deltaTime);
InputManager::MouseMotionCallbackArgs motion{
.pos = w.main.input.mouse.pos,

View file

@ -268,6 +268,10 @@ void DrawingProgram::input_pen_axis_callback(const InputManager::PenAxisCallback
drawTool->input_pen_axis_callback(axis);
}
std::optional<InputManager::TextBoxStartInfo> DrawingProgram::get_text_box_start_info() {
return drawTool->get_text_box_start_info();
}
void DrawingProgram::server_init_no_file() {
layerMan.server_init_no_file();
}

View file

@ -74,6 +74,7 @@ class DrawingProgram {
void input_pen_touch_callback(const InputManager::PenTouchCallbackArgs& touch);
void input_pen_motion_callback(const InputManager::PenMotionCallbackArgs& motion);
void input_pen_axis_callback(const InputManager::PenAxisCallbackArgs& axis);
std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
void set_right_click_popup_location(const Vector2f& newLoc);
void clear_right_click_popup();

View file

@ -65,3 +65,4 @@ void DrawingProgramToolBase::input_pen_button_callback(const InputManager::PenBu
void DrawingProgramToolBase::input_pen_touch_callback(const InputManager::PenTouchCallbackArgs& touch) {}
void DrawingProgramToolBase::input_pen_motion_callback(const InputManager::PenMotionCallbackArgs& motion) {}
void DrawingProgramToolBase::input_pen_axis_callback(const InputManager::PenAxisCallbackArgs& axis) {}
std::optional<InputManager::TextBoxStartInfo> DrawingProgramToolBase::get_text_box_start_info() { return std::nullopt; }

View file

@ -48,6 +48,7 @@ class DrawingProgramToolBase {
virtual void input_pen_touch_callback(const InputManager::PenTouchCallbackArgs& touch);
virtual void input_pen_motion_callback(const InputManager::PenMotionCallbackArgs& motion);
virtual void input_pen_axis_callback(const InputManager::PenAxisCallbackArgs& axis);
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
virtual ~DrawingProgramToolBase();
static std::unique_ptr<DrawingProgramToolBase> allocate_tool_type(DrawingProgram& drawP, DrawingProgramToolType t);
protected:

View file

@ -176,6 +176,12 @@ void EditTool::input_mouse_motion_callback(const InputManager::MouseMotionCallba
drawP.selection.input_mouse_motion_callback_modify_selection(motion);
}
std::optional<InputManager::TextBoxStartInfo> EditTool::get_text_box_start_info() {
if(objInfoBeingEdited)
return compEditTool->get_text_box_start_info();
return std::nullopt;
}
void EditTool::right_click_popup_gui(Toolbar& t, Vector2f popupPos) {
if(objInfoBeingEdited)
return compEditTool->right_click_popup_gui(t, popupPos);

View file

@ -33,6 +33,7 @@ class EditTool : public DrawingProgramToolBase {
virtual void input_key_callback(const InputManager::KeyCallbackArgs& key) override;
virtual void input_mouse_button_on_canvas_callback(const InputManager::MouseButtonCallbackArgs& button) override;
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info() override;
~EditTool();
void add_point_handle(const HandleData& handle);

View file

@ -16,5 +16,6 @@ void DrawingProgramEditToolBase::input_text_callback(const InputManager::TextCal
void DrawingProgramEditToolBase::input_key_callback(const InputManager::KeyCallbackArgs& key) {}
void DrawingProgramEditToolBase::input_mouse_button_on_canvas_callback(const InputManager::MouseButtonCallbackArgs& button, bool isDraggingPoint) {}
void DrawingProgramEditToolBase::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion, bool isDraggingPoint) {}
std::optional<InputManager::TextBoxStartInfo> DrawingProgramEditToolBase::get_text_box_start_info() { return std::nullopt; }
DrawingProgramEditToolBase::~DrawingProgramEditToolBase() {}

View file

@ -20,6 +20,7 @@ class DrawingProgramEditToolBase {
virtual void input_key_callback(const InputManager::KeyCallbackArgs& key);
virtual void input_mouse_button_on_canvas_callback(const InputManager::MouseButtonCallbackArgs& button, bool isDraggingPoint);
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion, bool isDraggingPoint);
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
virtual ~DrawingProgramEditToolBase();
protected:
DrawingProgram& drawP;

View file

@ -203,7 +203,7 @@ void TextBoxEditTool::edit_gui(Toolbar& t) {
}
void TextBoxEditTool::input_paste_callback(const CustomEvents::PasteEvent& paste) {
if(textboxID.has_value() && textboxID.value() == drawP.world.main.input.text.current_textbox_editing_id()) {
if(userInput && userInput->id == drawP.world.main.input.currentTextboxID.value()) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
if(userInput->input_paste_callback(paste)) {
commit_update_and_layout_func();
@ -213,7 +213,7 @@ void TextBoxEditTool::input_paste_callback(const CustomEvents::PasteEvent& paste
}
void TextBoxEditTool::input_text_key_callback(const InputManager::KeyCallbackArgs& key) {
if(textboxID.has_value() && textboxID.value() == drawP.world.main.input.text.current_textbox_editing_id()) {
if(userInput && userInput->id == drawP.world.main.input.currentTextboxID.value()) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
auto changes = userInput->input_key_callback(drawP.world.main.input, key);
if(changes.textEdited || changes.cursorChanged) {
@ -224,7 +224,7 @@ void TextBoxEditTool::input_text_key_callback(const InputManager::KeyCallbackArg
}
void TextBoxEditTool::input_text_callback(const InputManager::TextCallbackArgs& text) {
if(textboxID.has_value() && textboxID.value() == drawP.world.main.input.text.current_textbox_editing_id()) {
if(userInput && userInput->id == drawP.world.main.input.currentTextboxID.value()) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
userInput->add_text_to_textbox(text.str);
commit_update_and_layout_func();
@ -275,6 +275,25 @@ void TextBoxEditTool::input_mouse_motion_callback(const InputManager::MouseMotio
}
}
std::optional<InputManager::TextBoxStartInfo> TextBoxEditTool::get_text_box_start_info() {
if(userInput) {
InputManager::TextInputProperties tProps {
.inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT,
.capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE,
.autocorrect = true,
.multiline = true,
.androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_TEXT
};
return InputManager::TextBoxStartInfo {
.id = userInput->id,
.rect = std::nullopt,
.inputProperties = tProps,
};
}
return std::nullopt;
}
void TextBoxEditTool::right_click_popup_gui(Toolbar& t, Vector2f popupPos) {
using namespace GUIStuff;
using namespace ElementHelpers;
@ -375,8 +394,8 @@ void TextBoxEditTool::commit_edit_updates(std::any& prevData) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
a.d.editing = false;
comp->obj->commit_update(drawP);
if(textboxID.has_value())
drawP.world.main.input.remove_text_box(textboxID.value());
userInput = nullptr;
CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{});
}
TextBoxEditTool::TextBoxEditToolAllData TextBoxEditTool::get_all_data(const TextBoxCanvasComponent& a) {
@ -406,14 +425,8 @@ void TextBoxEditTool::edit_start(EditTool& editTool, std::any& prevData) {
comp->obj->commit_update(drawP);
userInput = std::make_unique<RichTextUserInput>(textbox, cur, currentModsPtr);
textboxID = drawP.world.main.input.set_text_box_back(std::nullopt, {
.inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT,
.capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE,
.autocorrect = true,
.multiline = true,
.androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_TEXT
});
userInput = std::make_unique<RichTextUserInput>(drawP.world.main.input.text_box_get_new_id(), textbox, cur, currentModsPtr);
CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{});
}
bool TextBoxEditTool::edit_update() {

View file

@ -22,9 +22,9 @@ class TextBoxEditTool : public DrawingProgramEditToolBase {
virtual void input_text_callback(const InputManager::TextCallbackArgs& text) override;
virtual void input_mouse_button_on_canvas_callback(const InputManager::MouseButtonCallbackArgs& button, bool isDraggingPoint) override;
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion, bool isDraggingPoint) override;
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info() override;
private:
std::unique_ptr<RichTextUserInput> userInput;
std::optional<unsigned> textboxID;
struct TextBoxEditToolAllData {
TextBoxCanvasComponent::Data textboxData;

View file

@ -163,3 +163,7 @@ void GUIHolder::input_finger_touch_callback(const InputManager::FingerTouchCallb
void GUIHolder::input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion) {
gui.input_finger_motion_callback(motion);
}
std::optional<InputManager::TextBoxStartInfo> GUIHolder::get_text_box_start_info() {
return gui.get_text_box_start_info();
}

View file

@ -33,6 +33,8 @@ class GUIHolder {
void input_finger_touch_callback(const InputManager::FingerTouchCallbackArgs& touch);
void input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion);
std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
float final_gui_scale();
private:
void calculate_final_gui_scale();

View file

@ -21,6 +21,7 @@ namespace GUIStuff {
input_mouse_motion_callback(InputManager::convert_finger_motion_to_mouse_motion(motion));
}
void Element::input_key_callback(const InputManager::KeyCallbackArgs& key) {}
std::optional<InputManager::TextBoxStartInfo> Element::get_text_box_start_info() { return std::nullopt; }
void Element::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {}

View file

@ -35,6 +35,7 @@ namespace GUIStuff {
virtual void input_finger_touch_callback(const InputManager::FingerTouchCallbackArgs& touch);
virtual void input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion);
virtual void input_key_callback(const InputManager::KeyCallbackArgs& key);
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
protected:
static SCollision::AABB<float> get_bb_from_command(Clay_RenderCommand* command);
std::optional<SCollision::AABB<float>> boundingBox;

View file

@ -23,16 +23,6 @@ template <typename T> struct TextBoxData {
std::function<void()> onDeselect;
};
struct TextEditData {
TextEditData(unsigned initTextboxInputID, const std::shared_ptr<RichText::TextBox>& initTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& initCursor):
textboxInputID(initTextboxInputID),
userInput(initTextBox, initCursor, nullptr)
{}
unsigned textboxInputID;
RichTextUserInput userInput;
};
template <typename T> class TextBox : public Element {
public:
TextBox(GUIManager& gui);
@ -47,6 +37,7 @@ template <typename T> class TextBox : public Element {
virtual void input_text_callback(const InputManager::TextCallbackArgs& text) override;
virtual void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info() override;
~TextBox();
private:
void populate_empty_textbox();
@ -64,7 +55,8 @@ template <typename T> class TextBox : public Element {
bool isEmptyText = false;
bool isHeld = false;
std::optional<TextEditData> edit;
std::unique_ptr<RichTextUserInput> edit;
std::optional<T> oldData;
std::string oldEmptyText;

View file

@ -1,5 +1,6 @@
#include "TextBox.decl.hpp"
#include "../GUIManager.hpp"
#include <SDL3/SDL_keyboard.h>
namespace GUIStuff {
@ -15,6 +16,11 @@ template <typename T> void TextBox<T>::layout(const Clay_ElementId& id, const Te
gui.invalidate_draw_element(this);
}
if(edit) {
SCollision::AABB<float> rect = {boundingBox.value().min * gui.io.guiScaleMultiplier, boundingBox.value().max * gui.io.guiScaleMultiplier};
gui.io.input->update_textbox_rectangle(edit->id, rect);
}
CLAY(id, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(static_cast<float>(io.fontSize * 2)), .height = CLAY_SIZING_FIXED(static_cast<float>(io.fontSize * 1.25f))}
@ -32,10 +38,6 @@ template <typename T> void TextBox<T>::update() {
oldDD.cur = *cur;
gui.invalidate_draw_element(this);
}
if(edit.has_value()) {
SCollision::AABB<float> rect = {boundingBox.value().min * gui.io.guiScaleMultiplier, boundingBox.value().max * gui.io.guiScaleMultiplier};
gui.io.input->update_textbox_rectangle(edit.value().textboxInputID, rect);
}
}
template <typename T> void TextBox<T>::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {
@ -80,30 +82,29 @@ template <typename T> void TextBox<T>::clay_draw(SkCanvas* canvas, UpdateInputDa
template <typename T> void TextBox<T>::select() {
if(!is_selected()) {
SCollision::AABB<float> rect = {boundingBox.value().min * gui.io.guiScaleMultiplier, boundingBox.value().max * gui.io.guiScaleMultiplier};
unsigned id = gui.io.input->set_text_box_front(rect, userInfo.textInputProps);
if(isEmptyText)
textbox->clear_text();
edit = TextEditData(id, textbox, cur);
edit = std::make_unique<RichTextUserInput>(gui.io.input->text_box_get_new_id(), textbox, cur, nullptr);
CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{});
}
}
template <typename T> void TextBox<T>::deselect() {
if(is_selected()) {
gui.io.input->remove_text_box(edit.value().textboxInputID);
edit = nullptr;
if(userInfo.onDeselect) userInfo.onDeselect();
CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{});
reset_textbox_text();
edit = std::nullopt;
populate_empty_textbox();
}
}
template <typename T> bool TextBox<T>::is_selected() const {
return edit.has_value();
return edit != nullptr;
}
template <typename T> void TextBox<T>::input_paste_callback(const CustomEvents::PasteEvent& paste) {
if(is_selected() && edit.value().userInput.input_paste_callback(paste))
if(is_selected() && edit->input_paste_callback(paste))
after_text_input_callback();
}
@ -118,7 +119,7 @@ template <typename T> void TextBox<T>::input_text_key_callback(const InputManage
});
}
else {
if(edit.value().userInput.input_key_callback(*gui.io.input, key).textEdited)
if(edit->input_key_callback(*gui.io.input, key).textEdited)
after_text_input_callback();
}
}
@ -150,7 +151,7 @@ template <typename T> void TextBox<T>::after_text_input_callback() {
template <typename T> void TextBox<T>::input_text_callback(const InputManager::TextCallbackArgs& text) {
if(is_selected()) {
edit.value().userInput.add_text_to_textbox(text.str);
edit->add_text_to_textbox(text.str);
after_text_input_callback();
}
}
@ -181,6 +182,18 @@ template <typename T> void TextBox<T>::input_mouse_motion_callback(const InputMa
textbox->process_mouse_left_button(*cur, motion.pos - boundingBox.value().min, 0, isHeld, gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held);
}
template <typename T> std::optional<InputManager::TextBoxStartInfo> TextBox<T>::get_text_box_start_info() {
if(edit) {
SCollision::AABB<float> rect = {boundingBox.value().min * gui.io.guiScaleMultiplier, boundingBox.value().max * gui.io.guiScaleMultiplier};
return InputManager::TextBoxStartInfo{
.id = edit->id,
.rect = rect,
.inputProperties = userInfo.textInputProps
};
}
return std::nullopt;
}
template <typename T> void TextBox<T>::init_textbox(UpdateInputData& io) {
if(!textbox) {
textbox = std::make_shared<RichText::TextBox>();
@ -212,7 +225,7 @@ template <typename T> bool TextBox<T>::update_data() {
template <typename T> TextBox<T>::~TextBox() {
if(is_selected())
gui.io.input->remove_text_box(edit.value().textboxInputID);
CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{});
}
}

View file

@ -757,6 +757,15 @@ void GUIManager::input_finger_motion_callback(InputManager::FingerMotionCallback
mouse_callback(motion.pos, [&motion] (ElementContainer* e) { e->elem->input_finger_motion_callback(motion); });
}
std::optional<InputManager::TextBoxStartInfo> GUIManager::get_text_box_start_info() {
for(ElementContainer* e : orderedElements) {
auto toRet = e->elem->get_text_box_start_info();
if(toRet)
return toRet;
}
return std::nullopt;
}
void GUIManager::mouse_callback(const Vector2f& mousePos, const std::function<void(ElementContainer*)>& f) {
cursorObstructed = false;
int16_t zIndexObstructed = 0;

View file

@ -108,6 +108,7 @@ class GUIManager {
void input_mouse_wheel_callback(InputManager::MouseWheelCallbackArgs wheel);
void input_finger_touch_callback(InputManager::FingerTouchCallbackArgs touch);
void input_finger_motion_callback(InputManager::FingerMotionCallbackArgs motion);
std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
bool cursor_obstructed() const;
private:

View file

@ -91,76 +91,33 @@ InputManager::InputManager(MainProgram& initMain):
keyAssignments = defaultKeyAssignments;
}
unsigned InputManager::Text::get_id() {
static unsigned nextID = 0;
while(std::find_if(textBoxes.begin(), textBoxes.end(), [&](const TextBoxInfo& info) { return info.id == nextID; }) != textBoxes.end())
InputManager::TextBoxID InputManager::text_box_get_new_id() {
static TextBoxID nextID = 0;
++nextID;
return nextID;
}
unsigned InputManager::set_text_box_front(const std::optional<SCollision::AABB<float>>& textboxRect, const TextInputProperties& inputProps) {
Text::TextBoxInfo textBoxInfo;
textBoxInfo.rect = textboxRect;
textBoxInfo.textInputProperties = inputProps;
textBoxInfo.id = text.get_id();
text.textBoxes.emplace_front(textBoxInfo);
update_accepting_input(main.window.sdlWindow);
return textBoxInfo.id;
}
void InputManager::refresh_receiving_text_box_input() {
std::optional<TextBoxStartInfo> startInfo = main.get_text_box_start_info();
if(startInfo.has_value()) {
if(!textPropID.has_value())
textPropID = SDL_CreateProperties();
currentTextboxID = startInfo->id;
unsigned InputManager::set_text_box_back(const std::optional<SCollision::AABB<float>>& textboxRect, const TextInputProperties& inputProps) {
Text::TextBoxInfo textBoxInfo;
textBoxInfo.rect = textboxRect;
textBoxInfo.textInputProperties = inputProps;
textBoxInfo.id = text.get_id();
text.textBoxes.emplace_back(textBoxInfo);
update_accepting_input(main.window.sdlWindow);
return textBoxInfo.id;
}
void InputManager::remove_text_box(unsigned id) {
std::erase_if(text.textBoxes, [id](Text::TextBoxInfo& info) {
return (info.id == id);
});
update_accepting_input(main.window.sdlWindow);
}
bool InputManager::Text::is_accepting_input() {
return !textBoxes.empty();
}
std::optional<unsigned> InputManager::Text::current_textbox_editing_id() {
if(textBoxes.empty())
return std::nullopt;
return textBoxes.front().id;
}
void InputManager::update_textbox_rectangle(unsigned textboxID, const SCollision::AABB<float>& textboxRect) {
auto it = std::find_if(text.textBoxes.begin(), text.textBoxes.end(), [&](const auto& t) {return t.id == textboxID;});
it->rect = textboxRect;
}
void InputManager::update_accepting_input(SDL_Window* window) {
if(!text.textBoxes.empty()) {
auto& textbox = text.textBoxes.front();
if(!text.propID.has_value())
text.propID = SDL_CreateProperties();
SDL_PropertiesID propIDVal = text.propID.value();
SDL_PropertiesID propIDVal = textPropID.value();
// There is a bug where decimal places disappear when textinput type is set to number on android. We'll just set those textinputs back to text type
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_TYPE_NUMBER, (textbox.textInputProperties.inputType == SDL_TEXTINPUT_TYPE_NUMBER) ? SDL_TEXTINPUT_TYPE_TEXT : textbox.textInputProperties.inputType);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, textbox.textInputProperties.capitalization);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN, textbox.textInputProperties.autocorrect);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN, textbox.textInputProperties.multiline);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER, (textbox.textInputProperties.androidInputType & ANDROIDTEXT_TYPE_CLASS_NUMBER) ? ANDROIDTEXT_TYPE_CLASS_TEXT : textbox.textInputProperties.androidInputType);
if(text.textBoxes.front().rect)
excludeIMEFromSafeArea = false;
else
excludeIMEFromSafeArea = true;
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_TYPE_NUMBER, (startInfo->inputProperties.inputType == SDL_TEXTINPUT_TYPE_NUMBER) ? SDL_TEXTINPUT_TYPE_TEXT : startInfo->inputProperties.inputType);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, startInfo->inputProperties.capitalization);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN, startInfo->inputProperties.autocorrect);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN, startInfo->inputProperties.multiline);
SDL_SetNumberProperty(propIDVal, SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER, (startInfo->inputProperties.androidInputType & ANDROIDTEXT_TYPE_CLASS_NUMBER) ? ANDROIDTEXT_TYPE_CLASS_TEXT : startInfo->inputProperties.androidInputType);
update_textbox_rectangle(currentTextboxID.value(), startInfo->rect);
#ifdef __ANDROID__
AndroidJNICalls::startTextInput();
AndroidJNICalls::startTextInput(startInfo->inputProperties.androidInputType);
#else
SDL_StartTextInputWithProperties(window, propIDVal);
SDL_StartTextInputWithProperties(main.window.sdlWindow, propIDVal);
#endif
#ifdef __EMSCRIPTEN__
@ -168,7 +125,8 @@ void InputManager::update_accepting_input(SDL_Window* window) {
#endif
}
else {
SDL_StopTextInput(window);
SDL_StopTextInput(main.window.sdlWindow);
currentTextboxID = std::nullopt;
excludeIMEFromSafeArea = false;
screenOffset = {0.0f, 0.0f};
#ifdef __EMSCRIPTEN__
@ -177,6 +135,32 @@ void InputManager::update_accepting_input(SDL_Window* window) {
}
}
bool InputManager::text_is_accepting_input() {
return currentTextboxID.has_value();
}
void InputManager::update_textbox_rectangle(TextBoxID textboxID, std::optional<SCollision::AABB<float>> textboxRect) {
if(currentTextboxID.has_value() && textboxID == currentTextboxID.value()) {
excludeIMEFromSafeArea = !textboxRect.has_value();
if(excludeIMEFromSafeArea)
screenOffset = {0.0f, 0.0f};
else {
constexpr float PADDING_BETWEEN_TEXTBOX_AND_IME = 15.0f;
std::scoped_lock a{safeAreaWithoutIMEMutex};
if(safeAreaWithoutIME.has_value()) {
float bottomY = textboxRect->max.y();
float yLimit = safeAreaWithoutIME->max.y() - PADDING_BETWEEN_TEXTBOX_AND_IME;
if(bottomY >= yLimit)
screenOffset = {0.0f, -(bottomY - yLimit)};
else
screenOffset = {0.0f, 0.0f};
}
else
screenOffset = {0.0f, 0.0f};
}
}
}
std::string InputManager::get_clipboard_str_SDL() {
char* data = SDL_GetClipboardText();
std::string toRet(data);
@ -908,7 +892,7 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
return;
}
bool acceptingTextInput = text.is_accepting_input();
bool acceptingTextInput = text_is_accepting_input();
switch(kPress) {
case SDLK_UP:
@ -1060,7 +1044,7 @@ bool InputManager::ctrl_or_meta_held() {
void InputManager::backend_key_up_update(const SDL_KeyboardEvent& e) {
auto kPress = e.key;
bool acceptingTextInput = text.is_accepting_input();
bool acceptingTextInput = text_is_accepting_input();
switch(kPress) {
case SDLK_UP:
@ -1141,23 +1125,6 @@ void InputManager::update() {
touch_finger_do_mouse_down();
touch.touchEventType = Touch::ONE_FINGER_EVENT;
}
constexpr float PADDING_BETWEEN_TEXTBOX_AND_IME = 15.0f;
if(!text.textBoxes.empty()) {
auto& r = text.textBoxes.front().rect;
std::scoped_lock a{safeAreaWithoutIMEMutex};
if(r.has_value() && safeAreaWithoutIME.has_value()) {
float bottomY = r.value().max.y();
float yLimit = safeAreaWithoutIME.value().max.y() - PADDING_BETWEEN_TEXTBOX_AND_IME;
if(bottomY >= yLimit)
screenOffset = {0.0f, -(bottomY - yLimit)};
else
screenOffset = {0.0f, 0.0f};
}
else
screenOffset = {0.0f, 0.0f};
}
else
screenOffset = {0.0f, 0.0f};
}
void InputManager::frame_reset(const Vector2i& windowSize) {

View file

@ -143,6 +143,7 @@ struct InputManager {
ANDROIDTEXT_TYPE_TEXT_VARIATION_WEB_PASSWORD = 0x000000e0
};
typedef int64_t TextBoxID;
struct TextInputProperties {
SDL_TextInputType inputType;
SDL_Capitalization capitalization;
@ -151,31 +152,27 @@ struct InputManager {
int androidInputType;
};
unsigned set_text_box_front(const std::optional<SCollision::AABB<float>>& textboxRect, const TextInputProperties& inputProps);
unsigned set_text_box_back(const std::optional<SCollision::AABB<float>>& textboxRect, const TextInputProperties& inputProps);
void remove_text_box(unsigned textboxID);
void update_accepting_input(SDL_Window* window);
void update_textbox_rectangle(unsigned textboxID, const SCollision::AABB<float>& textboxRect);
struct Text {
bool is_accepting_input();
std::optional<unsigned> current_textbox_editing_id();
void update_textbox_rectangle();
private:
struct TextBoxInfo {
struct TextBoxStartInfo {
TextBoxID id;
std::optional<SCollision::AABB<float>> rect;
TextInputProperties textInputProperties;
unsigned id;
TextInputProperties inputProperties;
// Android textbox info
std::string str;
int startCursorPos;
int endCursorPos;
};
std::optional<SDL_PropertiesID> propID;
void refresh_receiving_text_box_input();
void update_textbox_rectangle(TextBoxID textboxID, std::optional<SCollision::AABB<float>> textboxRect);
bool text_is_accepting_input();
std::deque<TextBoxInfo> textBoxes;
unsigned get_id();
void text_update_textbox_rectangle();
TextBoxID text_box_get_new_id();
void update_text_box_area_offset();
friend struct InputManager;
} text;
std::optional<TextBoxID> currentTextboxID;
std::optional<SDL_PropertiesID> textPropID;
#ifdef __APPLE__
SDL_Keymod CTRL_MOD = SDL_KMOD_GUI;

View file

@ -422,6 +422,13 @@ void MainProgram::input_window_scale_callback(const InputManager::WindowScaleCal
post_callback();
}
std::optional<InputManager::TextBoxStartInfo> MainProgram::get_text_box_start_info() {
auto toRet = g.get_text_box_start_info();
if(toRet)
return toRet;
return screen->get_text_box_start_info();
}
void MainProgram::toggle_full_screen() {
window.fullscreen = !window.fullscreen;
SDL_SetWindowFullscreen(window.sdlWindow, window.fullscreen);

View file

@ -158,6 +158,8 @@ class MainProgram {
void input_window_resize_callback(const InputManager::WindowResizeCallbackArgs& w);
void input_window_scale_callback(const InputManager::WindowScaleCallbackArgs& w);
std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
void create_new_tab(const CustomEvents::OpenInfiniPaintFileEvent& openFile);
void set_tab_to_close(World* world);
void switch_to_tab(size_t wIndex);

View file

@ -8,7 +8,8 @@ template <typename T> std::optional<T> shared_ptr_to_opt(const std::shared_ptr<T
return std::nullopt;
}
RichTextUserInput::RichTextUserInput(const std::shared_ptr<RichText::TextBox>& initTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& initCursor, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& initModMap):
RichTextUserInput::RichTextUserInput(InputManager::TextBoxID initId, const std::shared_ptr<RichText::TextBox>& initTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& initCursor, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& initModMap):
id(initId),
textBox(initTextBox),
cursor(initCursor),
modMap(initModMap)

View file

@ -6,7 +6,7 @@
class RichTextUserInput {
public:
RichTextUserInput(const std::shared_ptr<RichText::TextBox>& initTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& initCursor, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& initModMap);
RichTextUserInput(InputManager::TextBoxID initId, const std::shared_ptr<RichText::TextBox>& initTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& initCursor, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& initModMap);
struct Changes {
bool textEdited = false;
bool cursorChanged = false;
@ -16,6 +16,7 @@ class RichTextUserInput {
void add_text_to_textbox(const std::string& inputText);
void do_textbox_operation_with_undo(const std::function<void()>& func);
void add_textbox_undo(const RichText::TextBox::Cursor& prevCursor, const RichText::TextData& prevRichText);
const InputManager::TextBoxID id;
private:
std::shared_ptr<RichText::TextBox> textBox;
std::shared_ptr<RichText::TextBox::Cursor> cursor;

View file

@ -102,3 +102,7 @@ void DrawingProgramScreen::input_window_resize_callback(const InputManager::Wind
void DrawingProgramScreen::input_window_scale_callback(const InputManager::WindowScaleCallbackArgs& w) {
}
std::optional<InputManager::TextBoxStartInfo> DrawingProgramScreen::get_text_box_start_info() {
return main.world->get_text_box_start_info();
}

View file

@ -27,4 +27,5 @@ class DrawingProgramScreen : public Screen {
virtual void input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion) override;
virtual void input_window_resize_callback(const InputManager::WindowResizeCallbackArgs& w) override;
virtual void input_window_scale_callback(const InputManager::WindowScaleCallbackArgs& w) override;
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info() override;
};

View file

@ -30,5 +30,6 @@ void Screen::input_finger_motion_callback(const InputManager::FingerMotionCallba
void Screen::input_window_resize_callback(const InputManager::WindowResizeCallbackArgs& w) {}
void Screen::input_window_scale_callback(const InputManager::WindowScaleCallbackArgs& w) {}
void Screen::input_app_about_to_go_to_background_callback() {}
std::optional<InputManager::TextBoxStartInfo> Screen::get_text_box_start_info() { return std::nullopt; }
Screen::~Screen() {}

View file

@ -32,6 +32,7 @@ class Screen {
virtual void input_window_resize_callback(const InputManager::WindowResizeCallbackArgs& w);
virtual void input_window_scale_callback(const InputManager::WindowScaleCallbackArgs& w);
virtual void input_app_about_to_go_to_background_callback();
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
virtual ~Screen();
protected:

View file

@ -347,6 +347,12 @@ void World::input_multi_finger_motion_callback(const InputManager::MultiFingerMo
drawData.cam.input_multi_finger_motion_callback(*this, motion);
}
std::optional<InputManager::TextBoxStartInfo> World::get_text_box_start_info() {
if(!clientStillConnecting)
return drawProg.get_text_box_start_info();
return std::nullopt;
}
void World::send_chat_message(const std::string& message) {
if(!clientStillConnecting)
ownClientData->send_chat_message(ownClientData, *this, message);

View file

@ -109,6 +109,7 @@ class World {
void input_pen_axis_callback(const InputManager::PenAxisCallbackArgs& axis);
void input_multi_finger_touch_callback(const InputManager::MultiFingerTouchCallbackArgs& touch);
void input_multi_finger_motion_callback(const InputManager::MultiFingerMotionCallbackArgs& motion);
std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
private:
bool saveThumbnail = false;
bool hasUnsavedLocalChanges = false;

View file

@ -109,6 +109,8 @@ extern "C"
}
#endif
#include "AndroidJNICalls.hpp"
#include <unicode/udata.h>
std::string icudt; // Put this in global space so that it isn't deallocated
@ -369,6 +371,10 @@ void init_logs(MainStruct& mS) {
}
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
#ifdef __ANDROID__
std::scoped_lock a{AndroidJNICalls::globalCallMutex};
#endif
std::vector<std::filesystem::path> listOfFilesToOpenFromCommand;
for(int i = 1; i < argc; i++)
listOfFilesToOpenFromCommand.emplace_back(std::filesystem::canonical(std::filesystem::path(std::u8string_view(reinterpret_cast<char8_t*>(argv[i])))));
@ -592,6 +598,10 @@ void regular_draw(MainStruct& mS) {
}
SDL_AppResult SDL_AppIterate(void *appstate) {
#ifdef __ANDROID__
std::scoped_lock a{AndroidJNICalls::globalCallMutex};
#endif
std::chrono::steady_clock::time_point frameTimeStart = std::chrono::steady_clock::now();
MainStruct& mS = *((MainStruct*)appstate);
@ -631,6 +641,10 @@ SDL_AppResult SDL_AppIterate(void *appstate) {
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
#ifdef __ANDROID__
std::scoped_lock a{AndroidJNICalls::globalCallMutex};
#endif
MainStruct& mS = *((MainStruct*)appstate);
#ifdef NDEBUG
@ -788,6 +802,8 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
mS.m->input_open_infinipaint_file_callback(*CustomEvents::get_event<CustomEvents::OpenInfiniPaintFileEvent>());
else if(event->type == CustomEvents::AddFileToCanvasEvent::EVENT_NUM)
mS.m->input_add_file_to_canvas_callback(*CustomEvents::get_event<CustomEvents::AddFileToCanvasEvent>());
else if(event->type == CustomEvents::RefreshTextBoxInputEvent::EVENT_NUM)
mS.m->input.refresh_receiving_text_box_input();
break;
}
}