diff --git a/imgui-binding/src/main/java/imgui/ImGuiListClipper.java b/imgui-binding/src/main/java/imgui/ImGuiListClipper.java new file mode 100644 index 0000000..f6f5b3a --- /dev/null +++ b/imgui-binding/src/main/java/imgui/ImGuiListClipper.java @@ -0,0 +1,65 @@ +package imgui; + +import imgui.callbacks.ImListClipperCallback; + +/** + * Helper: Manually clip large list of items. + * If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse clipping based on visibility to save yourself from processing those items at all. + * The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. + * ImGui already clip items based on their bounds but it needs to measure text size to do so. Coarse clipping before submission makes this cost and your own data fetching/submission cost null. + * Usage: + *
+ *     ImGuiListClipper clipper(1000);  // we have 1000 elements, evenly spaced.
+ *     while (clipper.Step())
+ *         for (int i = clipper.DisplayStart; i {@code <} clipper.DisplayEnd; i++)
+ *             ImGui::Text("line number %d", i);
+ * 
+ * - Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height (step skipped if we passed a known height as second arg to constructor). + * - Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. + * - (Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.) + * - Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. + *

+ * BINDING NOTICE: + * It's impossible to implement the same API like in the original. Method {@link #forEach(int, int, ImListClipperCallback)} could be used instead. + *

+ *     ImGuiListClipper.forEach(1000, new ImListClipperCallback() {
+ *         public void accept(int index) {
+ *             ImGui.text(String.format("line number %d", index));
+ *         }
+ *     });
+ * 
+ */ +public final class ImGuiListClipper { + private ImGuiListClipper() { + } + + /*JNI + #include + #include "jni_callbacks.h" + */ + + /** + * @param itemsCount Use -1 to ignore (you can call Begin later). + * Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). + * @param callback action to do in iterations + */ + public static void forEach(final int itemsCount, final ImListClipperCallback callback) { + forEach(itemsCount, -1, callback); + } + + /** + * @param itemsCount Use -1 to ignore (you can call Begin later). + * Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). + * @param itemsHeight Use -1.0f to be calculated automatically on first step. + * Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). + * @param callback action to do in iterations + */ + public static native void forEach(int itemsCount, int itemsHeight, ImListClipperCallback callback); /* + ImGuiListClipper clipper(itemsCount); + while (clipper.Step()) { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + Jni::CallImListClipperCallback(env, callback, i); + } + } + */ +} diff --git a/imgui-binding/src/main/java/imgui/callbacks/ImListClipperCallback.java b/imgui-binding/src/main/java/imgui/callbacks/ImListClipperCallback.java new file mode 100644 index 0000000..de459bc --- /dev/null +++ b/imgui-binding/src/main/java/imgui/callbacks/ImListClipperCallback.java @@ -0,0 +1,8 @@ +package imgui.callbacks; + +/** + * Callback for {@link imgui.ImGuiListClipper} class. + */ +public abstract class ImListClipperCallback { + public abstract void accept(int index); +} diff --git a/imgui-binding/src/main/java/imgui/callbacks/ImStrConsumer.java b/imgui-binding/src/main/java/imgui/callbacks/ImStrConsumer.java index 89042e8..d0d2ac1 100644 --- a/imgui-binding/src/main/java/imgui/callbacks/ImStrConsumer.java +++ b/imgui-binding/src/main/java/imgui/callbacks/ImStrConsumer.java @@ -1,11 +1,8 @@ package imgui.callbacks; -import java.util.function.Consumer; - /** * Callback to supply Java string form native code. */ -public abstract class ImStrConsumer implements Consumer { - @Override +public abstract class ImStrConsumer { public abstract void accept(String str); } diff --git a/imgui-binding/src/main/java/imgui/callbacks/ImStrSupplier.java b/imgui-binding/src/main/java/imgui/callbacks/ImStrSupplier.java index 64781f1..d8f2be8 100644 --- a/imgui-binding/src/main/java/imgui/callbacks/ImStrSupplier.java +++ b/imgui-binding/src/main/java/imgui/callbacks/ImStrSupplier.java @@ -1,11 +1,8 @@ package imgui.callbacks; -import java.util.function.Supplier; - /** * Callback to get Java string from native code. */ -public abstract class ImStrSupplier implements Supplier { - @Override +public abstract class ImStrSupplier { public abstract String get(); } diff --git a/imgui-binding/src/main/native/jni_callbacks.cpp b/imgui-binding/src/main/native/jni_callbacks.cpp index da41dba..2eee850 100644 --- a/imgui-binding/src/main/native/jni_callbacks.cpp +++ b/imgui-binding/src/main/native/jni_callbacks.cpp @@ -1,23 +1,32 @@ #include "jni_callbacks.h" +jmethodID jImListClipperCallbackAcceptMID; + jmethodID jImStrConsumerAcceptMID; jmethodID jImStrSupplierGetMID; namespace Jni { -void InitCallbacks(JNIEnv* env) { - jclass jImStrConsumer = env->FindClass("imgui/callbacks/ImStrConsumer"); - jImStrConsumerAcceptMID = env->GetMethodID(jImStrConsumer, "accept", "(Ljava/lang/String;)V"); + void InitCallbacks(JNIEnv* env) { + jclass jImListClipperCallback = env->FindClass("imgui/callbacks/ImListClipperCallback"); + jImListClipperCallbackAcceptMID = env->GetMethodID(jImListClipperCallback, "accept", "(I)V"); - jclass jImStrSupplier = env->FindClass("imgui/callbacks/ImStrSupplier"); - jImStrSupplierGetMID = env->GetMethodID(jImStrSupplier, "get", "()Ljava/lang/String;"); -} + jclass jImStrConsumer = env->FindClass("imgui/callbacks/ImStrConsumer"); + jImStrConsumerAcceptMID = env->GetMethodID(jImStrConsumer, "accept", "(Ljava/lang/String;)V"); -void CallImStrConsumer(JNIEnv* env, jobject consumer, const char* str) { - env->CallVoidMethod(consumer, jImStrConsumerAcceptMID, env->NewStringUTF(str)); -} + jclass jImStrSupplier = env->FindClass("imgui/callbacks/ImStrSupplier"); + jImStrSupplierGetMID = env->GetMethodID(jImStrSupplier, "get", "()Ljava/lang/String;"); + } -jstring CallImStrSupplier(JNIEnv* env, jobject supplier) { - return (jstring)env->CallObjectMethod(supplier, jImStrSupplierGetMID); -} + void CallImListClipperCallback(JNIEnv* env, jobject consumer, int index) { + env->CallVoidMethod(consumer, jImListClipperCallbackAcceptMID, index); + } + + void CallImStrConsumer(JNIEnv* env, jobject consumer, const char* str) { + env->CallVoidMethod(consumer, jImStrConsumerAcceptMID, env->NewStringUTF(str)); + } + + jstring CallImStrSupplier(JNIEnv* env, jobject supplier) { + return (jstring)env->CallObjectMethod(supplier, jImStrSupplierGetMID); + } } diff --git a/imgui-binding/src/main/native/jni_callbacks.h b/imgui-binding/src/main/native/jni_callbacks.h index a85caac..4307b86 100644 --- a/imgui-binding/src/main/native/jni_callbacks.h +++ b/imgui-binding/src/main/native/jni_callbacks.h @@ -5,10 +5,13 @@ namespace Jni { -void InitCallbacks(JNIEnv* env); + void InitCallbacks(JNIEnv* env); -void CallImStrConsumer(JNIEnv* env, jobject consumer, const char* str); -jstring CallImStrSupplier(JNIEnv* env, jobject supplier); + void CallImListClipperCallback(JNIEnv* env, jobject consumer, int index); + + void CallImStrConsumer(JNIEnv* env, jobject consumer, const char* str); + + jstring CallImStrSupplier(JNIEnv* env, jobject supplier); } #endif diff --git a/imgui-binding/src/main/native/jni_common.cpp b/imgui-binding/src/main/native/jni_common.cpp index b2e8599..aa2a5ef 100644 --- a/imgui-binding/src/main/native/jni_common.cpp +++ b/imgui-binding/src/main/native/jni_common.cpp @@ -12,58 +12,58 @@ static JavaVM* jvm; namespace Jni { -void InitCommon(JNIEnv* env) { - env->GetJavaVM(&jvm); + void InitCommon(JNIEnv* env) { + env->GetJavaVM(&jvm); - jclass jImVec2Class = env->FindClass("imgui/ImVec2"); - imVec2XID = env->GetFieldID(jImVec2Class, "x", "F"); - imVec2YID = env->GetFieldID(jImVec2Class, "y", "F"); + jclass jImVec2Class = env->FindClass("imgui/ImVec2"); + imVec2XID = env->GetFieldID(jImVec2Class, "x", "F"); + imVec2YID = env->GetFieldID(jImVec2Class, "y", "F"); - jclass jImVec4Class = env->FindClass("imgui/ImVec4"); - imVec4XID = env->GetFieldID(jImVec4Class, "x", "F"); - imVec4YID = env->GetFieldID(jImVec4Class, "y", "F"); - imVec4ZID = env->GetFieldID(jImVec4Class, "z", "F"); - imVec4WID = env->GetFieldID(jImVec4Class, "w", "F"); -} + jclass jImVec4Class = env->FindClass("imgui/ImVec4"); + imVec4XID = env->GetFieldID(jImVec4Class, "x", "F"); + imVec4YID = env->GetFieldID(jImVec4Class, "y", "F"); + imVec4ZID = env->GetFieldID(jImVec4Class, "z", "F"); + imVec4WID = env->GetFieldID(jImVec4Class, "w", "F"); + } -JNIEnv* GetEnv() { - JNIEnv* env; - jint res = jvm->GetEnv((void**)(&env), JNI_VERSION_1_8); - assert(res == JNI_OK); - return env; -} + JNIEnv* GetEnv() { + JNIEnv* env; + jint res = jvm->GetEnv((void**)(&env), JNI_VERSION_1_8); + assert(res == JNI_OK); + return env; + } -void ImVec2Cpy(JNIEnv* env, ImVec2* src, jobject dst) { - env->SetFloatField(dst, imVec2XID, src->x); - env->SetFloatField(dst, imVec2YID, src->y); -} + void ImVec2Cpy(JNIEnv* env, ImVec2* src, jobject dst) { + env->SetFloatField(dst, imVec2XID, src->x); + env->SetFloatField(dst, imVec2YID, src->y); + } -void ImVec2Cpy(JNIEnv* env, ImVec2 src, jobject dst) { - env->SetFloatField(dst, imVec2XID, src.x); - env->SetFloatField(dst, imVec2YID, src.y); -} + void ImVec2Cpy(JNIEnv* env, ImVec2 src, jobject dst) { + env->SetFloatField(dst, imVec2XID, src.x); + env->SetFloatField(dst, imVec2YID, src.y); + } -void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2* dst) { - dst->x = env->GetFloatField(src, imVec2XID); - dst->y = env->GetFloatField(src, imVec2YID); -} + void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2* dst) { + dst->x = env->GetFloatField(src, imVec2XID); + dst->y = env->GetFloatField(src, imVec2YID); + } -void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2 dst) { - dst.x = env->GetFloatField(src, imVec2XID); - dst.y = env->GetFloatField(src, imVec2YID); -} + void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2 dst) { + dst.x = env->GetFloatField(src, imVec2XID); + dst.y = env->GetFloatField(src, imVec2YID); + } -void ImVec4Cpy(JNIEnv* env, ImVec4* src, jobject dst) { - env->SetFloatField(dst, imVec4XID, src->x); - env->SetFloatField(dst, imVec4YID, src->y); - env->SetFloatField(dst, imVec4ZID, src->z); - env->SetFloatField(dst, imVec4WID, src->w); -} + void ImVec4Cpy(JNIEnv* env, ImVec4* src, jobject dst) { + env->SetFloatField(dst, imVec4XID, src->x); + env->SetFloatField(dst, imVec4YID, src->y); + env->SetFloatField(dst, imVec4ZID, src->z); + env->SetFloatField(dst, imVec4WID, src->w); + } -void ImVec4Cpy(JNIEnv* env, ImVec4 src, jobject dst) { - env->SetFloatField(dst, imVec4XID, src.x); - env->SetFloatField(dst, imVec4YID, src.y); - env->SetFloatField(dst, imVec4ZID, src.z); - env->SetFloatField(dst, imVec4WID, src.w); -} + void ImVec4Cpy(JNIEnv* env, ImVec4 src, jobject dst) { + env->SetFloatField(dst, imVec4XID, src.x); + env->SetFloatField(dst, imVec4YID, src.y); + env->SetFloatField(dst, imVec4ZID, src.z); + env->SetFloatField(dst, imVec4WID, src.w); + } } diff --git a/imgui-binding/src/main/native/jni_common.h b/imgui-binding/src/main/native/jni_common.h index 63150e3..056cc92 100644 --- a/imgui-binding/src/main/native/jni_common.h +++ b/imgui-binding/src/main/native/jni_common.h @@ -6,18 +6,18 @@ namespace Jni { -void InitCommon(JNIEnv* env); + void InitCommon(JNIEnv* env); -JNIEnv* GetEnv(); + JNIEnv* GetEnv(); -void ImVec2Cpy(JNIEnv* env, ImVec2* src, jobject dst); -void ImVec2Cpy(JNIEnv* env, ImVec2 src, jobject dst); + void ImVec2Cpy(JNIEnv* env, ImVec2* src, jobject dst); + void ImVec2Cpy(JNIEnv* env, ImVec2 src, jobject dst); -void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2* dst); -void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2 dst); + void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2* dst); + void ImVec2Cpy(JNIEnv* env, jobject src, ImVec2 dst); -void ImVec4Cpy(JNIEnv* env, ImVec4* src, jobject dst); -void ImVec4Cpy(JNIEnv* env, ImVec4 src, jobject dst); + void ImVec4Cpy(JNIEnv* env, ImVec4* src, jobject dst); + void ImVec4Cpy(JNIEnv* env, ImVec4 src, jobject dst); } #endif