diff --git a/imgui-binding/build.gradle b/imgui-binding/build.gradle index ff0e07f..b2a55ac 100644 --- a/imgui-binding/build.gradle +++ b/imgui-binding/build.gradle @@ -9,6 +9,15 @@ plugins { sourceCompatibility = 1.8 +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2") +} + +test { + useJUnitPlatform() +} + assemble.dependsOn clean task generateLibs(type: GenerateLibs, dependsOn: assemble) diff --git a/imgui-binding/src/main/java/imgui/ImGui.java b/imgui-binding/src/main/java/imgui/ImGui.java index 6cfb794..2bdfca5 100644 --- a/imgui-binding/src/main/java/imgui/ImGui.java +++ b/imgui-binding/src/main/java/imgui/ImGui.java @@ -2397,7 +2397,7 @@ public final class ImGui { jfieldID imTextInputDataIsDirtyID; jfieldID imTextInputDataIsResizedID; - char* resizedBuf; + int resizeValue; struct InputTextCallbackUserData { JNIEnv* env; @@ -2434,6 +2434,10 @@ public final class ImGui { imTextInputDataIsResizedID = env->GetFieldID(jImInputTextDataClass, "isResized", "Z"); */ + private static native int nGetResizeValue(); /* + return resizeValue; + */ + public static boolean inputText(String label, ImString text) { return preInputText(false, label, text, 0, 0, 0); } @@ -2469,7 +2473,7 @@ public final class ImGui { if (inputData.isResized) { inputData.isResized = false; - text.set(nGetResizedStr(), true); + text.resize(nGetResizeValue(), true); } return hasInput; @@ -2485,31 +2489,27 @@ public final class ImGui { if (strlen(allowedChars) > 0) flags |= ImGuiInputTextFlags_CallbackCharFilter; - bool hasInput; + bool valueChanged; if (multiline) { - hasInput = ImGui::InputTextMultiline(label, buf, maxSize, ImVec2(width, height), flags, &TextEditCallbackStub, &userData); + valueChanged = ImGui::InputTextMultiline(label, buf, maxSize, ImVec2(width, height), flags, &TextEditCallbackStub, &userData); } else { - hasInput = ImGui::InputText(label, buf, maxSize, flags, &TextEditCallbackStub, &userData); + valueChanged = ImGui::InputText(label, buf, maxSize, flags, &TextEditCallbackStub, &userData); } - if (hasInput) { - int size = strlen(buf); + int size = strlen(buf); - if (((size + 1) > maxSize) && (flags & ImGuiInputTextFlags_CallbackResize)) { - env->SetBooleanField(textInputData, imTextInputDataIsResizedID, true); - resizedBuf = buf; - } + if ((flags & ImGuiInputTextFlags_CallbackResize) && ((size + 1) > maxSize)) { + env->SetBooleanField(textInputData, imTextInputDataIsResizedID, true); + resizeValue = size; + } + if (valueChanged) { env->SetIntField(textInputData, imTextInputDataSizeID, size); env->SetBooleanField(textInputData, imTextInputDataIsDirtyID, true); } - return hasInput; - */ - - private static native String nGetResizedStr(); /* - return env->NewStringUTF(resizedBuf); + return valueChanged; */ public static boolean inputFloat(String label, ImFloat v) { diff --git a/imgui-binding/src/main/java/imgui/ImGuiIO.java b/imgui-binding/src/main/java/imgui/ImGuiIO.java index bb9de4e..a4746fa 100644 --- a/imgui-binding/src/main/java/imgui/ImGuiIO.java +++ b/imgui-binding/src/main/java/imgui/ImGuiIO.java @@ -261,8 +261,8 @@ public final class ImGuiIO { // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) /*JNI - jobject _setClipboardTextCallback = nullptr; - jobject _getClipboardTextCallback = nullptr; + jobject _setClipboardTextCallback = NULL; + jobject _getClipboardTextCallback = NULL; void setClipboardTextStub(void* userData, const char* text) { Jni::CallImStrConsumer(Jni::GetEnv(), _setClipboardTextCallback, text); @@ -276,7 +276,7 @@ public final class ImGuiIO { */ public native void setSetClipboardTextFn(ImStrConsumer setClipboardTextCallback); /* - if (_setClipboardTextCallback != nullptr) { + if (_setClipboardTextCallback != NULL) { env->DeleteGlobalRef(_setClipboardTextCallback); } @@ -285,7 +285,7 @@ public final class ImGuiIO { */ public native void setGetClipboardTextFn(ImStrSupplier getClipboardTextCallback); /* - if (_getClipboardTextCallback != nullptr) { + if (_getClipboardTextCallback != NULL) { env->DeleteGlobalRef(_getClipboardTextCallback); } diff --git a/imgui-binding/src/main/java/imgui/ImString.java b/imgui-binding/src/main/java/imgui/ImString.java index f196577..8fc0524 100644 --- a/imgui-binding/src/main/java/imgui/ImString.java +++ b/imgui-binding/src/main/java/imgui/ImString.java @@ -6,10 +6,23 @@ import java.util.Objects; * Wrapper for {@link String} to use inside of th Dear ImGui input widgets. */ public final class ImString { - private static final short DEFAULT_LENGTH = 100; - private static final short CARET_LEN = 1; - private static final short DEFAULT_RESIZE = 10; + /** + * Default size of the inner buffer, if {@link ImString} created with a constructor without args. + */ + public static final short DEFAULT_LENGTH = 100; + /** + * Size of ImGui caret which is shown during the input text focus. + */ + public static final short CARET_LEN = 1; + /** + * Default resize value, used to set up {@link ImString#resizeFactor}. + */ + public static final short DEFAULT_RESIZE = 10; + /** + * Configuration class to setup some specific behaviour for current string. + * This is useful when string used inside of ImGui#InputText*() methods. + */ public final ImGuiInputTextData inputData = new ImGuiInputTextData(); /** * String will be resized to the value equal to a new size plus this resize factor. @@ -19,19 +32,35 @@ public final class ImString { byte[] data; private String text = ""; + /** + * Creates an {@link ImString} instance with {@link ImString#DEFAULT_LENGTH} size for the inner buffer. + */ public ImString() { this(DEFAULT_LENGTH); } + /** + * Creates an {@link ImString} instance with provided size for the inner buffer. + * @param length size of the inner buffer to use + */ public ImString(final int length) { data = new byte[length + CARET_LEN]; } + /** + * Creates an {@link ImString} instance from provided string. + * Inner buffer size will be equal to the length of the string + {@link ImString#CARET_LEN}. + * @param text string to create a new {@link ImString} + */ public ImString(final String text) { - this(text.length()); - set(text); + set(text, true, 0); } + /** + * Create an {@link ImString} instance from provided string with custom size for the inner buffer. + * @param text string to a create a new {@link ImString} + * @param length custom size for the inner buffer + */ public ImString(final String text, final int length) { this(length); set(text); @@ -46,15 +75,46 @@ public final class ImString { } public void set(final String value) { - set(value.getBytes()); + set(value, inputData.isResizable, resizeFactor); } public void set(final String value, final boolean resize) { - final byte[] str = value.getBytes(); - if (resize && data.length - CARET_LEN < str.length) { - data = new byte[str.length + resizeFactor + CARET_LEN]; + set(value, resize, resizeFactor); + } + + public void set(final String value, final boolean resize, final int resizeValue) { + final byte[] valueBuff = value.getBytes(); + final int currentLen = data == null ? 0 : data.length; + byte[] newBuff = null; + + if (resize && (currentLen - CARET_LEN) < valueBuff.length) { + newBuff = new byte[valueBuff.length + resizeValue + CARET_LEN]; + inputData.size = valueBuff.length; } - set(str); + + if (newBuff == null) { + newBuff = new byte[currentLen]; + inputData.size = Math.max(0, Math.min(valueBuff.length, currentLen - CARET_LEN)); + } + + System.arraycopy(valueBuff, 0, newBuff, 0, Math.min(valueBuff.length, newBuff.length - CARET_LEN)); + data = newBuff; + inputData.isDirty = true; + } + + public void resize(final int newSize) { + resize(newSize, false); + } + + public void resize(final int newSize, final boolean respectResizeFactor) { + if (newSize < data.length) { + throw new IllegalArgumentException("New size should be greater than current size of the buffer"); + } + + final int size = newSize + CARET_LEN + (respectResizeFactor ? resizeFactor : 0); + final byte[] newBuffer = new byte[size]; + System.arraycopy(data, 0, newBuffer, 0, data.length); + data = newBuffer; } /** @@ -73,13 +133,6 @@ public final class ImString { return data.length; } - private void set(final byte[] str) { - final int len = Math.min(str.length, data.length); - System.arraycopy(str, 0, data, 0, len); - inputData.isDirty = true; - inputData.size = len; - } - @Override public String toString() { return get(); diff --git a/imgui-binding/src/test/java/imgui/ImStringTest.java b/imgui-binding/src/test/java/imgui/ImStringTest.java new file mode 100644 index 0000000..ec3dd77 --- /dev/null +++ b/imgui-binding/src/test/java/imgui/ImStringTest.java @@ -0,0 +1,55 @@ +package imgui; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ImStringTest { + @Test + public void testCreation() { + ImString str = new ImString(10); + assertEquals("", str.get()); + assertEquals(0, str.getLength(), "String length should be 0, since no data was provided"); + assertEquals(10 + 1, str.getBufferSize(), "Buffer size must be 'length + 1', +1 - size of the ImGui caret"); + + str = new ImString("test"); + assertEquals("test", str.get()); + assertEquals(4, str.getLength()); + assertEquals(4 + 1, str.getBufferSize()); + + str = new ImString("test", 10); + assertEquals("test", str.get()); + assertEquals(4, str.getLength()); + assertEquals(10 + 1, str.getBufferSize()); + } + + @Test + public void testGet() { + final ImString str = new ImString("test"); + assertEquals("test", str.get()); + assertEquals(str.get(), str.toString(), "ImString#toString() must return value which should be equal to ImString#get()"); + } + + @Test + public void testSet() { + final ImString str = new ImString(3); + assertEquals("", str.get()); + assertEquals(0, str.getLength()); + assertEquals(3 + 1, str.getBufferSize()); + + str.set("test"); + assertEquals("tes", str.get()); + assertEquals(3, str.getLength()); + assertEquals(3 + 1, str.getBufferSize()); + + str.set("test", true, 0); + assertEquals("test", str.get()); + assertEquals(4, str.getLength()); + assertEquals(4 + 1, str.getBufferSize()); + + str.set("0123456789", true); + assertEquals("0123456789", str.get()); + assertEquals(10, str.getLength()); + assertEquals(10 + str.resizeFactor + 1, str.getBufferSize()); + } +}