diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro index edae1f8..bcd9ebc 100644 --- a/android-project/app/proguard-rules.pro +++ b/android-project/app/proguard-rules.pro @@ -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 { diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java index 53fb881..c656fbb 100644 --- a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java @@ -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; diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java index 756e8a9..6e61d01 100644 --- a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java @@ -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(); @@ -63,7 +73,7 @@ class InfiniPaintTextBoxInputConnection extends BaseInputConnection */ /* - * Return DOES still generate a key event, however. So rather than using it as the 'click a button' key + * Return DOES still generate a key event, however. So rather than using it as the 'click a button' key * as we do with physical keyboards, let's just use it to hide the keyboard. */ @@ -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; -// } -//} - diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java index 45a3251..6c1114a 100644 --- a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java @@ -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; diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/IntPair.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/IntPair.java new file mode 100644 index 0000000..4562719 --- /dev/null +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/IntPair.java @@ -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; +} diff --git a/src/AndroidJNICalls.cpp b/src/AndroidJNICalls.cpp index 6b9be73..ac77b7e 100644 --- a/src/AndroidJNICalls.cpp +++ b/src/AndroidJNICalls.cpp @@ -11,15 +11,15 @@ namespace AndroidJNICalls { InputManager* globalInputManager; std::mutex globalCallMutex; - void startTextInput() { + void startTextInput(int input_type) { JNIEnv* env = static_cast(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() -} -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() +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); + } } -#endif +#endif \ No newline at end of file diff --git a/src/AndroidJNICalls.hpp b/src/AndroidJNICalls.hpp index f0cd54a..e8cac97 100644 --- a/src/AndroidJNICalls.hpp +++ b/src/AndroidJNICalls.hpp @@ -8,7 +8,7 @@ namespace AndroidJNICalls { extern InputManager* globalInputManager; extern std::mutex globalCallMutex; - void startTextInput(); + void startTextInput(int input_type); } #endif diff --git a/src/CustomEvents.cpp b/src/CustomEvents.cpp index fcef3fe..8740cbd 100644 --- a/src/CustomEvents.cpp +++ b/src/CustomEvents.cpp @@ -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; } diff --git a/src/CustomEvents.hpp b/src/CustomEvents.hpp index 191af57..76bd934 100644 --- a/src/CustomEvents.hpp +++ b/src/CustomEvents.hpp @@ -67,4 +67,8 @@ namespace CustomEvents { std::filesystem::path filePath; Vector2f pos; }; + + struct RefreshTextBoxInputEvent { + static uint32_t EVENT_NUM; + }; } diff --git a/src/DrawCamera.cpp b/src/DrawCamera.cpp index f12232f..294b82b 100755 --- a/src/DrawCamera.cpp +++ b/src/DrawCamera.cpp @@ -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() * 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() * 0.5f), w.main.deltaTime); InputManager::MouseMotionCallbackArgs motion{ .pos = w.main.input.mouse.pos, diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 511fa73..cc5e9ed 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -268,6 +268,10 @@ void DrawingProgram::input_pen_axis_callback(const InputManager::PenAxisCallback drawTool->input_pen_axis_callback(axis); } +std::optional DrawingProgram::get_text_box_start_info() { + return drawTool->get_text_box_start_info(); +} + void DrawingProgram::server_init_no_file() { layerMan.server_init_no_file(); } diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index 722cd74..b206039 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -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 get_text_box_start_info(); void set_right_click_popup_location(const Vector2f& newLoc); void clear_right_click_popup(); diff --git a/src/DrawingProgram/Tools/DrawingProgramToolBase.cpp b/src/DrawingProgram/Tools/DrawingProgramToolBase.cpp index 88c1933..530b5c8 100644 --- a/src/DrawingProgram/Tools/DrawingProgramToolBase.cpp +++ b/src/DrawingProgram/Tools/DrawingProgramToolBase.cpp @@ -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 DrawingProgramToolBase::get_text_box_start_info() { return std::nullopt; } diff --git a/src/DrawingProgram/Tools/DrawingProgramToolBase.hpp b/src/DrawingProgram/Tools/DrawingProgramToolBase.hpp index 3e06aaa..7db536c 100644 --- a/src/DrawingProgram/Tools/DrawingProgramToolBase.hpp +++ b/src/DrawingProgram/Tools/DrawingProgramToolBase.hpp @@ -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 get_text_box_start_info(); virtual ~DrawingProgramToolBase(); static std::unique_ptr allocate_tool_type(DrawingProgram& drawP, DrawingProgramToolType t); protected: diff --git a/src/DrawingProgram/Tools/EditTool.cpp b/src/DrawingProgram/Tools/EditTool.cpp index 151b598..c4f00a8 100644 --- a/src/DrawingProgram/Tools/EditTool.cpp +++ b/src/DrawingProgram/Tools/EditTool.cpp @@ -176,6 +176,12 @@ void EditTool::input_mouse_motion_callback(const InputManager::MouseMotionCallba drawP.selection.input_mouse_motion_callback_modify_selection(motion); } +std::optional 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); diff --git a/src/DrawingProgram/Tools/EditTool.hpp b/src/DrawingProgram/Tools/EditTool.hpp index 32538d9..fc1a1bf 100644 --- a/src/DrawingProgram/Tools/EditTool.hpp +++ b/src/DrawingProgram/Tools/EditTool.hpp @@ -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 get_text_box_start_info() override; ~EditTool(); void add_point_handle(const HandleData& handle); diff --git a/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.cpp b/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.cpp index 49eddd8..548c707 100644 --- a/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.cpp +++ b/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.cpp @@ -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 DrawingProgramEditToolBase::get_text_box_start_info() { return std::nullopt; } DrawingProgramEditToolBase::~DrawingProgramEditToolBase() {} diff --git a/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.hpp b/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.hpp index c11f9b4..1be5229 100644 --- a/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.hpp +++ b/src/DrawingProgram/Tools/EditTools/DrawingProgramEditToolBase.hpp @@ -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 get_text_box_start_info(); virtual ~DrawingProgramEditToolBase(); protected: DrawingProgram& drawP; diff --git a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp index 0a168d8..780b0e4 100644 --- a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp +++ b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp @@ -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(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(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(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 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(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(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(drawP.world.main.input.text_box_get_new_id(), textbox, cur, currentModsPtr); + CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{}); } bool TextBoxEditTool::edit_update() { diff --git a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.hpp b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.hpp index 4b19ea7..988a79a 100644 --- a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.hpp +++ b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.hpp @@ -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 get_text_box_start_info() override; private: std::unique_ptr userInput; - std::optional textboxID; struct TextBoxEditToolAllData { TextBoxCanvasComponent::Data textboxData; diff --git a/src/GUIHolder.cpp b/src/GUIHolder.cpp index b15d509..841c221 100644 --- a/src/GUIHolder.cpp +++ b/src/GUIHolder.cpp @@ -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 GUIHolder::get_text_box_start_info() { + return gui.get_text_box_start_info(); +} diff --git a/src/GUIHolder.hpp b/src/GUIHolder.hpp index 6fed5ea..047dca4 100644 --- a/src/GUIHolder.hpp +++ b/src/GUIHolder.hpp @@ -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 get_text_box_start_info(); + float final_gui_scale(); private: void calculate_final_gui_scale(); diff --git a/src/GUIStuff/Elements/Element.cpp b/src/GUIStuff/Elements/Element.cpp index ace042a..73138aa 100644 --- a/src/GUIStuff/Elements/Element.cpp +++ b/src/GUIStuff/Elements/Element.cpp @@ -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 Element::get_text_box_start_info() { return std::nullopt; } void Element::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {} diff --git a/src/GUIStuff/Elements/Element.hpp b/src/GUIStuff/Elements/Element.hpp index a296297..b62a477 100644 --- a/src/GUIStuff/Elements/Element.hpp +++ b/src/GUIStuff/Elements/Element.hpp @@ -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 get_text_box_start_info(); protected: static SCollision::AABB get_bb_from_command(Clay_RenderCommand* command); std::optional> boundingBox; diff --git a/src/GUIStuff/Elements/TextBox.decl.hpp b/src/GUIStuff/Elements/TextBox.decl.hpp index b597b88..cb20436 100644 --- a/src/GUIStuff/Elements/TextBox.decl.hpp +++ b/src/GUIStuff/Elements/TextBox.decl.hpp @@ -23,16 +23,6 @@ template struct TextBoxData { std::function onDeselect; }; - -struct TextEditData { - TextEditData(unsigned initTextboxInputID, const std::shared_ptr& initTextBox, const std::shared_ptr& initCursor): - textboxInputID(initTextboxInputID), - userInput(initTextBox, initCursor, nullptr) - {} - unsigned textboxInputID; - RichTextUserInput userInput; -}; - template class TextBox : public Element { public: TextBox(GUIManager& gui); @@ -47,6 +37,7 @@ template 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 get_text_box_start_info() override; ~TextBox(); private: void populate_empty_textbox(); @@ -64,7 +55,8 @@ template class TextBox : public Element { bool isEmptyText = false; bool isHeld = false; - std::optional edit; + + std::unique_ptr edit; std::optional oldData; std::string oldEmptyText; diff --git a/src/GUIStuff/Elements/TextBox.impl.hpp b/src/GUIStuff/Elements/TextBox.impl.hpp index de37b0c..f623355 100644 --- a/src/GUIStuff/Elements/TextBox.impl.hpp +++ b/src/GUIStuff/Elements/TextBox.impl.hpp @@ -1,5 +1,6 @@ #include "TextBox.decl.hpp" #include "../GUIManager.hpp" +#include namespace GUIStuff { @@ -15,6 +16,11 @@ template void TextBox::layout(const Clay_ElementId& id, const Te gui.invalidate_draw_element(this); } + if(edit) { + SCollision::AABB 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(io.fontSize * 2)), .height = CLAY_SIZING_FIXED(static_cast(io.fontSize * 1.25f))} @@ -32,10 +38,6 @@ template void TextBox::update() { oldDD.cur = *cur; gui.invalidate_draw_element(this); } - if(edit.has_value()) { - SCollision::AABB rect = {boundingBox.value().min * gui.io.guiScaleMultiplier, boundingBox.value().max * gui.io.guiScaleMultiplier}; - gui.io.input->update_textbox_rectangle(edit.value().textboxInputID, rect); - } } template void TextBox::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) { @@ -80,30 +82,29 @@ template void TextBox::clay_draw(SkCanvas* canvas, UpdateInputDa template void TextBox::select() { if(!is_selected()) { - SCollision::AABB 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(gui.io.input->text_box_get_new_id(), textbox, cur, nullptr); + CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{}); } } template void TextBox::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 bool TextBox::is_selected() const { - return edit.has_value(); + return edit != nullptr; } template void TextBox::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 void TextBox::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 void TextBox::after_text_input_callback() { template void TextBox::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 void TextBox::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 std::optional TextBox::get_text_box_start_info() { + if(edit) { + SCollision::AABB 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 void TextBox::init_textbox(UpdateInputData& io) { if(!textbox) { textbox = std::make_shared(); @@ -212,7 +225,7 @@ template bool TextBox::update_data() { template TextBox::~TextBox() { if(is_selected()) - gui.io.input->remove_text_box(edit.value().textboxInputID); + CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{}); } } diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index 932bdd8..910d557 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -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 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& f) { cursorObstructed = false; int16_t zIndexObstructed = 0; diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp index 43d7ce7..2d13c23 100644 --- a/src/GUIStuff/GUIManager.hpp +++ b/src/GUIStuff/GUIManager.hpp @@ -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 get_text_box_start_info(); bool cursor_obstructed() const; private: diff --git a/src/InputManager.cpp b/src/InputManager.cpp index fdca52d..c36ea62 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -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()) - ++nextID; +InputManager::TextBoxID InputManager::text_box_get_new_id() { + static TextBoxID nextID = 0; + ++nextID; return nextID; } -unsigned InputManager::set_text_box_front(const std::optional>& 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 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>& 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 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& 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> 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) { diff --git a/src/InputManager.hpp b/src/InputManager.hpp index 8d2f656..faea74d 100644 --- a/src/InputManager.hpp +++ b/src/InputManager.hpp @@ -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>& textboxRect, const TextInputProperties& inputProps); - unsigned set_text_box_back(const std::optional>& 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& textboxRect); - - struct Text { - bool is_accepting_input(); - std::optional current_textbox_editing_id(); - void update_textbox_rectangle(); + struct TextBoxStartInfo { + TextBoxID id; + std::optional> rect; + TextInputProperties inputProperties; - private: - struct TextBoxInfo { - std::optional> rect; - TextInputProperties textInputProperties; - unsigned id; - }; + // Android textbox info + std::string str; + int startCursorPos; + int endCursorPos; + }; - std::optional propID; + void refresh_receiving_text_box_input(); + void update_textbox_rectangle(TextBoxID textboxID, std::optional> textboxRect); + bool text_is_accepting_input(); - std::deque 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 currentTextboxID; + std::optional textPropID; #ifdef __APPLE__ SDL_Keymod CTRL_MOD = SDL_KMOD_GUI; diff --git a/src/MainProgram.cpp b/src/MainProgram.cpp index 041e9d7..1a6fbda 100755 --- a/src/MainProgram.cpp +++ b/src/MainProgram.cpp @@ -422,6 +422,13 @@ void MainProgram::input_window_scale_callback(const InputManager::WindowScaleCal post_callback(); } +std::optional 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); diff --git a/src/MainProgram.hpp b/src/MainProgram.hpp index 3483fd9..fb5f346 100644 --- a/src/MainProgram.hpp +++ b/src/MainProgram.hpp @@ -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 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); diff --git a/src/RichTextUserInput.cpp b/src/RichTextUserInput.cpp index 74c34bb..6b51816 100644 --- a/src/RichTextUserInput.cpp +++ b/src/RichTextUserInput.cpp @@ -8,7 +8,8 @@ template std::optional shared_ptr_to_opt(const std::shared_ptr& initTextBox, const std::shared_ptr& initCursor, const std::shared_ptr& initModMap): +RichTextUserInput::RichTextUserInput(InputManager::TextBoxID initId, const std::shared_ptr& initTextBox, const std::shared_ptr& initCursor, const std::shared_ptr& initModMap): + id(initId), textBox(initTextBox), cursor(initCursor), modMap(initModMap) diff --git a/src/RichTextUserInput.hpp b/src/RichTextUserInput.hpp index 0706e1d..f88f4a3 100644 --- a/src/RichTextUserInput.hpp +++ b/src/RichTextUserInput.hpp @@ -6,7 +6,7 @@ class RichTextUserInput { public: - RichTextUserInput(const std::shared_ptr& initTextBox, const std::shared_ptr& initCursor, const std::shared_ptr& initModMap); + RichTextUserInput(InputManager::TextBoxID initId, const std::shared_ptr& initTextBox, const std::shared_ptr& initCursor, const std::shared_ptr& 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& func); void add_textbox_undo(const RichText::TextBox::Cursor& prevCursor, const RichText::TextData& prevRichText); + const InputManager::TextBoxID id; private: std::shared_ptr textBox; std::shared_ptr cursor; diff --git a/src/Screens/DrawingProgramScreen.cpp b/src/Screens/DrawingProgramScreen.cpp index 868f069..8531370 100644 --- a/src/Screens/DrawingProgramScreen.cpp +++ b/src/Screens/DrawingProgramScreen.cpp @@ -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 DrawingProgramScreen::get_text_box_start_info() { + return main.world->get_text_box_start_info(); +} diff --git a/src/Screens/DrawingProgramScreen.hpp b/src/Screens/DrawingProgramScreen.hpp index 8a23a17..1faaff5 100644 --- a/src/Screens/DrawingProgramScreen.hpp +++ b/src/Screens/DrawingProgramScreen.hpp @@ -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 get_text_box_start_info() override; }; diff --git a/src/Screens/Screen.cpp b/src/Screens/Screen.cpp index 60aaf07..3fd4c75 100644 --- a/src/Screens/Screen.cpp +++ b/src/Screens/Screen.cpp @@ -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 Screen::get_text_box_start_info() { return std::nullopt; } Screen::~Screen() {} diff --git a/src/Screens/Screen.hpp b/src/Screens/Screen.hpp index a14f2bc..df09674 100644 --- a/src/Screens/Screen.hpp +++ b/src/Screens/Screen.hpp @@ -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 get_text_box_start_info(); virtual ~Screen(); protected: diff --git a/src/World.cpp b/src/World.cpp index 270dbad..6a21d2d 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -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 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); diff --git a/src/World.hpp b/src/World.hpp index 0385850..dfe1b86 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -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 get_text_box_start_info(); private: bool saveThumbnail = false; bool hasUnsavedLocalChanges = false; diff --git a/src/main.cpp b/src/main.cpp index 0db54e0..7c17835 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -109,6 +109,8 @@ extern "C" } #endif +#include "AndroidJNICalls.hpp" + #include 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 listOfFilesToOpenFromCommand; for(int i = 1; i < argc; i++) listOfFilesToOpenFromCommand.emplace_back(std::filesystem::canonical(std::filesystem::path(std::u8string_view(reinterpret_cast(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()); else if(event->type == CustomEvents::AddFileToCanvasEvent::EVENT_NUM) mS.m->input_add_file_to_canvas_callback(*CustomEvents::get_event()); + else if(event->type == CustomEvents::RefreshTextBoxInputEvent::EVENT_NUM) + mS.m->input.refresh_receiving_text_box_input(); break; } }