[API] Implement ImGuiFileDialog extension (#89)

* Initial setup for ImGuiFileDialog extension

* Need to resolve linker errors. Working on PaneFun callback.

* Remove jni callback code that is not compiling to expose linker error.

* Callbacks for paneFun not yet implemented. Linker error still present, seems that some header is being included multiple times.

* Fixed linker errors. Added example for demo.

* Add support for multiple callbacks

* Callbacks work
This commit is contained in:
Trevor Flynn authored and GitHub committed 2021-11-16 13:07:13 +02:00
1 parent ec68f20e0c
commit 0bbbc2e037
11 files changed
+444 -1

No files matched your search

+3
View File
@@ -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
@@ -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') }
@@ -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<String, String> 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();
}
}
+6
View File
@@ -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);
}
}
}
@@ -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<jlong>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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 =&gt; 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&lt;FileName, FilePathName&gt;
* <p>
* 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<String, String> getSelection(); /*
//Get the map from ImGuiFileDialog
std::map<std::string, std::string> 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, "<init>", "(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
* <p>
* 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<jlong>(ImGuiFileDialog::Instance()->GetUserDatas());
*/
}
@@ -0,0 +1,5 @@
package imgui.extension.imguifiledialog.callback;
public abstract class ImGuiFileDialogPaneFun {
public abstract void paneFun(String filter, long userDatas, boolean canContinue);
}
@@ -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);
}
@@ -0,0 +1,6 @@
#pragma once
#include <string>
#include <map>
#include "_common.h"
#include "ImGuiFileDialog.h"
@@ -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);
}
}
@@ -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);
}