diff --git a/.gitmodules b/.gitmodules index 508622b..d9ea751 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "include/ImGuiColorTextEdit"] path = include/ImGuiColorTextEdit url = https://github.com/BalazsJako/ImGuiColorTextEdit +[submodule "include/ImGuiFileDialog"] + path = include/ImGuiFileDialog + url = https://github.com/aiekick/ImGuiFileDialog diff --git a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy index ddc16b1..18876dc 100644 --- a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy +++ b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy @@ -47,13 +47,22 @@ class GenerateLibs extends DefaultTask { // Copy ImGui h/cpp files project.copy { CopySpec spec -> ['include/imgui', 'include/imnodes', 'include/imgui-node-editor', - 'include/imguizmo', 'include/implot', 'include/ImGuiColorTextEdit'].each { + 'include/imguizmo', 'include/implot', 'include/ImGuiColorTextEdit', + 'include/ImGuiFileDialog'].each { spec.from(project.rootProject.file(it)) { CopySpec s -> s.include('*.h', '*.cpp', '*.inl') } } spec.from(project.rootProject.file('imgui-binding/src/main/native')) spec.into(jniDir) } + //Copy dirent for ImGuiFileDialog + project.copy { CopySpec spec -> + ['include/ImGuiFileDialog/dirent'].each { + spec.from(project.rootProject.file(it)) { CopySpec s -> s.include('*.h', '*.cpp', '*.inl') } + } + spec.into(jniDir + '/dirent') + } + if (withFreeType) { project.copy { CopySpec spec -> spec.from(project.rootProject.file('include/imgui/misc/freetype')) { CopySpec it -> it.include('*.h', '*.cpp') } diff --git a/example/src/main/java/ExampleImGuiFileDialog.java b/example/src/main/java/ExampleImGuiFileDialog.java new file mode 100644 index 0000000..afafbba --- /dev/null +++ b/example/src/main/java/ExampleImGuiFileDialog.java @@ -0,0 +1,61 @@ +import imgui.extension.imguifiledialog.ImGuiFileDialog; +import imgui.extension.imguifiledialog.callback.ImGuiFileDialogPaneFun; +import imgui.extension.imguifiledialog.flag.ImGuiFileDialogFlags; +import imgui.flag.ImGuiCond; +import imgui.internal.ImGui; +import imgui.type.ImBoolean; + +import java.awt.*; +import java.net.URI; +import java.util.Map; + +public class ExampleImGuiFileDialog { + private static final String URL = "https://github.com/aiekick/ImGuiFileDialog"; + + private static Map selection = null; + private static long userData = 0; + private static ImGuiFileDialogPaneFun callback = new ImGuiFileDialogPaneFun() { + @Override + public void paneFun(String filter, long userDatas, boolean canContinue) { + ImGui.text("Filter: " + filter); + } + }; + + public static void show(ImBoolean showImGuiFileDialogDemo) { + ImGui.setNextWindowSize(800, 200, ImGuiCond.Once); + ImGui.setNextWindowPos(ImGui.getMainViewport().getPosX() + 100, ImGui.getMainViewport().getPosY() + 100, ImGuiCond.Once); + if (ImGui.begin("ImGuiFileDialogDemo Demo", showImGuiFileDialogDemo)) { + ImGui.text("This a demo for ImGuiFileDialog"); + + ImGui.alignTextToFramePadding(); + ImGui.text("Repo:"); + ImGui.sameLine(); + if (ImGui.button(URL)) { + try { + Desktop.getDesktop().browse(new URI(URL)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (ImGui.button("Browse File")) { + ImGuiFileDialog.openModal("browse-key", "Choose File", ".java", ".", callback, 250, 1, 42, ImGuiFileDialogFlags.None); + } + + if (ImGuiFileDialog.display("browse-key", ImGuiFileDialogFlags.None, 200, 400, 800, 600)) { + if (ImGuiFileDialog.isOk()) { + selection = ImGuiFileDialog.getSelection(); + userData = ImGuiFileDialog.getUserDatas(); + } + ImGuiFileDialog.close(); + } + } + + if (selection != null && !selection.isEmpty()) { + ImGui.text("Selected: " + selection.values().stream().findFirst().get()); + ImGui.text("User Data: " + userData); + } + + ImGui.end(); + } +} diff --git a/example/src/main/java/Extra.java b/example/src/main/java/Extra.java index 4dfe008..3953f12 100644 --- a/example/src/main/java/Extra.java +++ b/example/src/main/java/Extra.java @@ -10,6 +10,7 @@ public class Extra { private static final ImBoolean SHOW_IMPLOT_DEMO_WINDOW = new ImBoolean(false); private static final ImBoolean SHOW_IMGUIZMO_DEMO = new ImBoolean(false); private static final ImBoolean SHOW_IMGUI_COLOR_TEXT_EDIT_WINDOW = new ImBoolean(false); + private static final ImBoolean SHOW_IMGUI_FILE_DIALOG_WINDOW = new ImBoolean(false); private static final Graph GRAPH = new Graph(); @@ -22,6 +23,7 @@ public class Extra { ImGui.checkbox("Show ImPlot Demo Window", SHOW_IMPLOT_DEMO_WINDOW); ImGui.checkbox("Show ImGuizmo Demo Window", SHOW_IMGUIZMO_DEMO); ImGui.checkbox("Show ImGuiColorTextEdit Demo Window", SHOW_IMGUI_COLOR_TEXT_EDIT_WINDOW); + ImGui.checkbox("Show ImGuiFileDialog Demo Window", SHOW_IMGUI_FILE_DIALOG_WINDOW); if (SHOW_DEMO_WINDOW.get()) { ImGui.showDemoWindow(SHOW_DEMO_WINDOW); @@ -50,5 +52,9 @@ public class Extra { if (SHOW_IMGUI_COLOR_TEXT_EDIT_WINDOW.get()) { ExampleImGuiColorTextEdit.show(SHOW_IMGUI_COLOR_TEXT_EDIT_WINDOW); } + + if (SHOW_IMGUI_FILE_DIALOG_WINDOW.get()) { + ExampleImGuiFileDialog.show(SHOW_IMGUI_FILE_DIALOG_WINDOW); + } } } diff --git a/imgui-binding/src/main/java/imgui/extension/imguifiledialog/ImGuiFileDialog.java b/imgui-binding/src/main/java/imgui/extension/imguifiledialog/ImGuiFileDialog.java new file mode 100644 index 0000000..f386f5c --- /dev/null +++ b/imgui-binding/src/main/java/imgui/extension/imguifiledialog/ImGuiFileDialog.java @@ -0,0 +1,302 @@ +package imgui.extension.imguifiledialog; + +import imgui.extension.imguifiledialog.callback.ImGuiFileDialogPaneFun; + +import java.util.HashMap; + +/** + * ImGuiFileDialog extension for ImGui + * Repo: https://github.com/aiekick/ImGuiFileDialog + */ +public final class ImGuiFileDialog { + + private ImGuiFileDialog() { + + } + + /*JNI + #include "_imguifiledialog.h" + + jobject imgfdPaneFunCallback = NULL; + const void paneFunCallback(const char* filter, void* user_data, bool* can_we_continue) { + if (imgfdPaneFunCallback != NULL) { + JNIEnv* env = Jni::GetEnv(); + std::string vKey = ImGuiFileDialog::Instance()->GetOpenedKey(); + jlong ret_user_datas = reinterpret_cast(user_data); + Jni::CallImGuiFileDialogPaneFun(env, imgfdPaneFunCallback, filter, ret_user_datas, *can_we_continue); + } + } + + #define IMGFD_PANE_FUN_METHOD_TMPL()\ + if (imgfdPaneFunCallback != NULL) {\ + env->DeleteGlobalRef(imgfdPaneFunCallback);\ + }\ + imgfdPaneFunCallback = env->NewGlobalRef(vSidePane); + */ + + /** + * open simple dialog (path and fileName can be specified) + * + * @param vKey key dialog + * @param vTitle title + * @param vFilters filters + * @param vPath path + * @param vFileName default file name + * @param vCountSelectionMax count selection max + * @param vUserDatas user datas (can be retrieved in pane) + * @param vFlags ImGuiFileDialogFlags + */ + public static native void openDialog(String vKey, String vTitle, String vFilters, String vPath, String vFileName, + int vCountSelectionMax, long vUserDatas, int vFlags); /* + ImGuiFileDialog::Instance()->OpenDialog(vKey, vTitle, vFilters, vPath, vFileName, vCountSelectionMax, reinterpret_cast(vUserDatas), vFlags); + */ + + + /** + * open simple modal (path and fileName can be specified) + * + * @param vKey key dialog + * @param vTitle title + * @param vFilters filters + * @param vPath path + * @param vFileName default file name + * @param vCountSelectionMax count selection max + * @param vUserDatas user datas (can be retrieved in pane) + * @param vFlags ImGuiFileDialogFlags + */ + public static native void openModal(String vKey, String vTitle, String vFilters, String vPath, String vFileName, + int vCountSelectionMax, long vUserDatas, int vFlags); /* + ImGuiFileDialog::Instance()->OpenModal(vKey, vTitle, vFilters, vPath, vFileName, vCountSelectionMax, reinterpret_cast(vUserDatas), vFlags); + */ + + + /** + * open simple modal (path and filename are obtained from filePathName) + * + * @param vKey key dialog + * @param vTitle title + * @param vFilters filters + * @param vFilePathName file path name (will be decomposed in path and fileName) + * @param vCountSelectionMax count selection max + * @param vUserDatas user datas (can be retrieved in pane) + * @param vFlags ImGuiFileDialogFlags + */ + public static native void openModal(String vKey, String vTitle, String vFilters, String vFilePathName, + int vCountSelectionMax, long vUserDatas, int vFlags); /* + ImGuiFileDialog::Instance()->OpenModal(vKey, vTitle, vFilters, vFilePathName, vCountSelectionMax, reinterpret_cast(vUserDatas), vFlags); + */ + + + /** + * open modal with custom right pane (path and fileName can be specified) + * + * @param vKey key dialog + * @param vTitle title + * @param vFilters filters + * @param vPath path + * @param vFileName default file name + * @param vSidePane side pane + * @param vSidePaneWidth side pane width + * @param vCountSelectionMax count selection max + * @param vUserDatas user datas (can be retrieved in pane) + * @param vFlags ImGuiFileDialogFlags + */ + public static native void openModal(String vKey, String vTitle, String vFilters, String vPath, String vFileName, ImGuiFileDialogPaneFun vSidePane, + float vSidePaneWidth, int vCountSelectionMax, long vUserDatas, int vFlags); /* + IMGFD_PANE_FUN_METHOD_TMPL() + ImGuiFileDialog::Instance()->OpenModal(vKey, vTitle, vFilters, vPath, vFileName, paneFunCallback, vSidePaneWidth, vCountSelectionMax, reinterpret_cast(vUserDatas), vFlags); + */ + + + /** + * open modal with custom right pane (path and filename are obtained from filePathName) + * + * @param vKey key dialog + * @param vTitle title + * @param vFilters filters + * @param vFilePathName file path name (will be decomposed in path and fileName) + * @param vSidePane side pane + * @param vSidePaneWidth side pane width + * @param vCountSelectionMax count selection max + * @param vUserDatas user datas (can be retrieved in pane) + * @param vFlags ImGuiFileDialogFlags + */ + public static native void openModal(String vKey, String vTitle, String vFilters, String vFilePathName, ImGuiFileDialogPaneFun vSidePane, + float vSidePaneWidth, int vCountSelectionMax, long vUserDatas, int vFlags); /* + IMGFD_PANE_FUN_METHOD_TMPL() + ImGuiFileDialog::Instance()->OpenModal(vKey, vTitle, vFilters, vFilePathName, paneFunCallback, vSidePaneWidth, vCountSelectionMax, reinterpret_cast(vUserDatas), vFlags); + */ + + /** + * Display / Close dialog form + * Display the dialog. return true if a result was obtained (Ok or not) + * + * @param vKey key dialog to display (if not the same key as defined by OpenDialog/Modal => no opening) + * @param vFlags ImGuiWindowFlags + * @param vMinSizeX minimal size constraint for the ImGuiWindow + * @param vMinSizeY minimal size constraint for the ImGuiWindow + * @param vMaxSizeX maximal size constraint for the ImGuiWindow + * @param vMaxSizeY maximal size constraint for the ImGuiWindow + * @return true if a result was obtained (Ok or not) + */ + public static native boolean display(String vKey, int vFlags, float vMinSizeX, float vMinSizeY, float vMaxSizeX, float vMaxSizeY); /* + return ImGuiFileDialog::Instance()->Display(vKey, vFlags, ImVec2(vMinSizeX, vMinSizeY), ImVec2(vMaxSizeX, vMaxSizeY)); + */ + + + /** + * close dialog + */ + public static native void close(); /* + ImGuiFileDialog::Instance()->Close(); + */ + + + /** + * say if the dialog key was already opened this frame + * + * @param vKey key dialog + * @return if the dialog key was already opened this frame + */ + public static native boolean wasOpenedThisFrame(String vKey); /* + return ImGuiFileDialog::Instance()->WasOpenedThisFrame(vKey); + */ + + + /** + * say if the dialog was already opened this frame + * + * @return if the dialog was already opened this frame + */ + public static native boolean wasOpenedThisFrame(); /* + return ImGuiFileDialog::Instance()->WasOpenedThisFrame(); + */ + + + /** + * say if the key is opened + * + * @param vKey key dialog + * @return if the key is opened + */ + public static native boolean isOpened(String vKey); /* + return ImGuiFileDialog::Instance()->IsOpened(vKey); + */ + + + /** + * say if the dialog is opened somewhere + * + * @return if the dialog is opened somewhere + */ + public static native boolean isOpened(); /* + return ImGuiFileDialog::Instance()->IsOpened(); + */ + + + /** + * return the dialog key who is opened, return nothing if not opened + * + * @return the dialog key who is opened or nothing is not opened + */ + public static native String getOpenedKey(); /* + return env->NewStringUTF(ImGuiFileDialog::Instance()->GetOpenedKey().c_str()); + */ + + + /** + * true: Dialog Closed with Ok result / false: Dialog closed with cancel result + * + * @return True if the dialog closed with Ok result, or false with cancel result + */ + public static native boolean isOk(); /* + return ImGuiFileDialog::Instance()->IsOk(); + */ + + + /** + * Open File behavior : will return selection via a map<FileName, FilePathName> + *

+ * For example, if a file is selected, say test.txt. Then the key value pair will be: + * 'test.txt', '/some/path/to/test.txt' + * + * @return Map of FileName to FilePathName in key,value pair + */ + public static native HashMap getSelection(); /* + //Get the map from ImGuiFileDialog + std::map mMap = ImGuiFileDialog::Instance()->GetSelection(); + + env->PushLocalFrame(mMap.size() * 2); // Expands stack size to not overflow + + //Get reference to java's HashMap + jclass hashMapClass = env->FindClass("java/util/HashMap"); + jmethodID hashMapInit = env->GetMethodID(hashMapClass, "", "(I)V"); + jobject hashMapObj = env->NewObject(hashMapClass, hashMapInit, mMap.size()); + jmethodID hashMapPut = env->GetMethodID(hashMapClass, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + //Copy key,value pairs from map to hashmap + for (auto it : mMap) + { + env->CallObjectMethod(hashMapObj, hashMapPut, + env->NewStringUTF(it.first.c_str()), + env->NewStringUTF(it.second.c_str()) + ); + } + + env->PopLocalFrame(hashMapObj); //Cleanup stack + + return hashMapObj; + */ + + + /** + * Save File behavior : will always return the content of the field with current filter extention and current path + */ + public static native String getFilePathName(); /* + return env->NewStringUTF(ImGuiFileDialog::Instance()->GetFilePathName().c_str()); + */ + + + /** + * Save File behavior : will always return the content of the field with current filter extension + * + * @return the content of the field with current filter extension + */ + public static native String getCurrentFileName(); /* + return env->NewStringUTF(ImGuiFileDialog::Instance()->GetCurrentFileName().c_str()); + */ + + + /** + * will return current path + * + * @return the current path + */ + public static native String getCurrentPath(); /* + return env->NewStringUTF(ImGuiFileDialog::Instance()->GetCurrentPath().c_str()); + */ + + + /** + * will return selected filter + * + * @return the selected filter + */ + public static native String getCurrentFilter(); /* + return env->NewStringUTF(ImGuiFileDialog::Instance()->GetCurrentFilter().c_str()); + */ + + + /** + * will return user datas sent with Open Dialog/Modal + *

+ * Can be used to pass a long value to the dialog and get the value back. + * This long value can be a pointer to a native data structure. + * + * @return user datas sent with Open Dialog/Modal + */ + public static native long getUserDatas(); /* + return reinterpret_cast(ImGuiFileDialog::Instance()->GetUserDatas()); + */ +} diff --git a/imgui-binding/src/main/java/imgui/extension/imguifiledialog/callback/ImGuiFileDialogPaneFun.java b/imgui-binding/src/main/java/imgui/extension/imguifiledialog/callback/ImGuiFileDialogPaneFun.java new file mode 100644 index 0000000..f9745ea --- /dev/null +++ b/imgui-binding/src/main/java/imgui/extension/imguifiledialog/callback/ImGuiFileDialogPaneFun.java @@ -0,0 +1,5 @@ +package imgui.extension.imguifiledialog.callback; + +public abstract class ImGuiFileDialogPaneFun { + public abstract void paneFun(String filter, long userDatas, boolean canContinue); +} diff --git a/imgui-binding/src/main/java/imgui/extension/imguifiledialog/flag/ImGuiFileDialogFlags.java b/imgui-binding/src/main/java/imgui/extension/imguifiledialog/flag/ImGuiFileDialogFlags.java new file mode 100644 index 0000000..fab4759 --- /dev/null +++ b/imgui-binding/src/main/java/imgui/extension/imguifiledialog/flag/ImGuiFileDialogFlags.java @@ -0,0 +1,40 @@ +package imgui.extension.imguifiledialog.flag; + +public final class ImGuiFileDialogFlags { + + private ImGuiFileDialogFlags() { + + } + + public static final int None = 0; + + /** + * show confirm to overwrite dialog + */ + public static final int ConfirmOverwrite = (1 << 0); + + /** + * dont show hidden file (file starting with a .) + */ + public static final int DontShowHiddenFiles = (1 << 1); + + /** + * disable the create directory button + */ + public static final int DisableCreateDirectoryButton = (1 << 2); + + /** + * hide column file type + */ + public static final int HideColumnType = (1 << 3); + + /** + * hide column file size + */ + public static final int HideColumnSize = (1 << 4); + + /** + * hide column file date + */ + public static final int HideColumnDate = (1 << 5); +} diff --git a/imgui-binding/src/main/native/_imguifiledialog.h b/imgui-binding/src/main/native/_imguifiledialog.h new file mode 100644 index 0000000..6044b3a --- /dev/null +++ b/imgui-binding/src/main/native/_imguifiledialog.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include "_common.h" +#include "ImGuiFileDialog.h" diff --git a/imgui-binding/src/main/native/jni_callbacks.cpp b/imgui-binding/src/main/native/jni_callbacks.cpp index 3e6445f..06d320f 100644 --- a/imgui-binding/src/main/native/jni_callbacks.cpp +++ b/imgui-binding/src/main/native/jni_callbacks.cpp @@ -12,6 +12,7 @@ jmethodID jImPlatformFuncViewportStringAcceptMID; jmethodID jImPlatformFuncViewportSuppImVec2GetMID; jmethodID jImPlatformFuncViewportSuppBooleanGetMID; jmethodID jImPlatformFuncViewportSuppFloatGetMID; +jmethodID jImGuiFileDialogPaneFunMID; namespace Jni { @@ -45,6 +46,9 @@ namespace Jni jclass jImPlatformFuncViewportSuppFloat = env->FindClass("imgui/callback/ImPlatformFuncViewportSuppFloat"); jImPlatformFuncViewportSuppFloatGetMID = env->GetMethodID(jImPlatformFuncViewportSuppFloat, "get", "(Limgui/ImGuiViewport;)F"); + + jclass jImGuiFileDialogPaneFun = env->FindClass("imgui/extension/imguifiledialog/callback/ImGuiFileDialogPaneFun"); + jImGuiFileDialogPaneFunMID = env->GetMethodID(jImGuiFileDialogPaneFun, "paneFun", "(Ljava/lang/String;JZ)V"); } void CallImListClipperCallback(JNIEnv* env, jobject consumer, int index) { @@ -86,4 +90,8 @@ namespace Jni jfloat CallImPlatformFuncViewportSuppFloat(JNIEnv* env, jobject func, jobject vp) { return (jfloat)env->CallFloatMethod(func, jImPlatformFuncViewportSuppFloatGetMID, vp); } + + void CallImGuiFileDialogPaneFun(JNIEnv* env, jobject func, const char* filter, long user_datas, bool canWeContinue) { + env->CallVoidMethod(func, jImGuiFileDialogPaneFunMID, env->NewStringUTF(filter), user_datas, canWeContinue); + } } diff --git a/imgui-binding/src/main/native/jni_callbacks.h b/imgui-binding/src/main/native/jni_callbacks.h index 5f44501..9724fe5 100644 --- a/imgui-binding/src/main/native/jni_callbacks.h +++ b/imgui-binding/src/main/native/jni_callbacks.h @@ -25,4 +25,6 @@ namespace Jni jboolean CallImPlatformFuncViewportSuppBoolean(JNIEnv* env, jobject func, jobject vp); jfloat CallImPlatformFuncViewportSuppFloat(JNIEnv* env, jobject func, jobject vp); + + void CallImGuiFileDialogPaneFun(JNIEnv* env, jobject func, const char* filter, long user_datas, bool canWeContinue); } diff --git a/include/ImGuiFileDialog b/include/ImGuiFileDialog new file mode 160000 index 0000000..007091a --- /dev/null +++ b/include/ImGuiFileDialog @@ -0,0 +1 @@ +Subproject commit 007091af18c69a2eed15cf5016951eb481a1de79