From 5478497a2f6eeb86f0d361d83f5141e1c2b2c223 Mon Sep 17 00:00:00 2001 From: Oras Phongpanangam Date: Thu, 30 Jul 2026 14:06:16 -0400 Subject: [PATCH 1/5] add file dialog support and example usage --- .gitmodules | 3 + deps/CMakeLists.txt | 10 +-- deps/imgui/CMakeLists.txt | 9 +++ deps/imgui/ImGuiFileDialog | 1 + examples/demo-app/demo_app.cpp | 71 ++++++++++++++++++++ include/polyscope/imgui_file_dialog_config.h | 8 +++ src/CMakeLists.txt | 1 + 7 files changed, 98 insertions(+), 5 deletions(-) create mode 160000 deps/imgui/ImGuiFileDialog create mode 100644 include/polyscope/imgui_file_dialog_config.h diff --git a/.gitmodules b/.gitmodules index 442ccb09..49262670 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,6 @@ [submodule "deps/imgui/ImGuizmo"] path = deps/imgui/ImGuizmo url = https://github.com/nmwsharp/ImGuizmo.git +[submodule "deps/imgui/ImGuiFileDialog"] + path = deps/imgui/ImGuiFileDialog + url = https://github.com/aiekick/ImGuiFileDialog.git diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index b54ff63e..827051a1 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -22,6 +22,11 @@ if(NOT TARGET glm::glm) set_target_properties(glm PROPERTIES LINKER_LANGUAGE CXX) endif() +## IconFontCppHeaders +if(NOT TARGET IconFontCppHeaders) + add_subdirectory(IconFontCppHeaders) +endif() + ## Imgui if(NOT TARGET imgui) add_subdirectory(imgui) @@ -41,8 +46,3 @@ endif() if(NOT TARGET stb) add_subdirectory(stb) endif() - -## IconFontCppHeaders -if(NOT TARGET IconFontCppHeaders) - add_subdirectory(IconFontCppHeaders) -endif() diff --git a/deps/imgui/CMakeLists.txt b/deps/imgui/CMakeLists.txt index b4a8b365..e061724d 100644 --- a/deps/imgui/CMakeLists.txt +++ b/deps/imgui/CMakeLists.txt @@ -36,6 +36,11 @@ list(APPEND SRCS ImGuizmo/ImGuizmo.cpp ) +# ImGuiFileDialog sources +list(APPEND SRCS + ImGuiFileDialog/ImGuiFileDialog.cpp + ) + # Add per-backend sources if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") @@ -63,6 +68,10 @@ endif() add_library(imgui ${SRCS}) target_compile_features(imgui PUBLIC cxx_std_11) set_target_properties(imgui PROPERTIES POSITION_INDEPENDENT_CODE TRUE) +target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../include") +target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/ImGuiFileDialog/") +target_link_libraries(imgui PUBLIC IconFontCppHeaders) +target_compile_definitions(imgui PUBLIC CUSTOM_IMGUIFILEDIALOG_CONFIG="polyscope/imgui_file_dialog_config.h") # Per-backend settings if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") diff --git a/deps/imgui/ImGuiFileDialog b/deps/imgui/ImGuiFileDialog new file mode 160000 index 00000000..ee77f190 --- /dev/null +++ b/deps/imgui/ImGuiFileDialog @@ -0,0 +1 @@ +Subproject commit ee77f190861193a995435c388682f1512f6fd29a diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index afe26351..075dfc17 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -19,6 +19,9 @@ #include "polyscope/volume_grid.h" #include "polyscope/volume_mesh.h" +#include "ImGuiFileDialog.h" +#include "IconFontCppHeaders/IconsLucide.h" + #include #include #include @@ -868,6 +871,11 @@ void processFile(std::string filename) { } } +enum class DemoFileDialogAction { None, OpenOne, OpenMany, Save, Directory }; + +IGFD::FileDialog demoFileDialog; +DemoFileDialogAction demoFileDialogAction = DemoFileDialogAction::None; + void callback() { static int numPoints = 2000; @@ -880,6 +888,69 @@ void callback() { ImGui::InputInt("num points", &numPoints); ImGui::InputFloat("param value", ¶m); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode("File dialogs")) { + if (ImGui::Button(ICON_LC_FOLDER_OPEN " Open mesh file")) { + IGFD::FileDialogConfig config; + config.path = "."; + config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode; + demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_FOLDER_OPEN " Open a mesh", + "Mesh files{.obj,.ply,.mesh},All files{.*}", config); + demoFileDialogAction = DemoFileDialogAction::OpenOne; + } + + if (ImGui::Button(ICON_LC_FILES " Open multiple files")) { + IGFD::FileDialogConfig config; + config.path = "."; + config.countSelectionMax = 0; + config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode | + ImGuiFileDialogFlags_DontShowHiddenFiles | ImGuiFileDialogFlags_CaseInsensitiveExtentionFiltering; + demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_FILES " Open mesh files", ".obj,.ply,.mesh", config); + demoFileDialogAction = DemoFileDialogAction::OpenMany; + } + + if (ImGui::Button(ICON_LC_SAVE " Choose output file")) { + IGFD::FileDialogConfig config; + config.path = "."; + config.fileName = "output.obj"; + config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode | + ImGuiFileDialogFlags_ConfirmOverwrite; + demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_SAVE " Choose an output file", ".obj", config); + demoFileDialogAction = DemoFileDialogAction::Save; + } + + if (ImGui::Button(ICON_LC_FOLDER " Choose directory")) { + IGFD::FileDialogConfig config; + config.path = "."; + config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode; + demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_FOLDER " Choose a directory", nullptr, config); + demoFileDialogAction = DemoFileDialogAction::Directory; + } + ImGui::TreePop(); + } + + if (demoFileDialogAction != DemoFileDialogAction::None) { + const ImVec2 viewportSize = ImGui::GetMainViewport()->WorkSize; + const ImVec2 minDialogSize(viewportSize.x * 0.5f, viewportSize.y * 0.5f); + const ImVec2 maxDialogSize(viewportSize.x * 0.9f, viewportSize.y * 0.9f); + if (demoFileDialog.Display("DemoFileDialog", ImGuiWindowFlags_NoCollapse, minDialogSize, maxDialogSize)) { + if (demoFileDialog.IsOk()) { + if (demoFileDialogAction == DemoFileDialogAction::OpenOne || + demoFileDialogAction == DemoFileDialogAction::OpenMany) { + for (const auto& entry : demoFileDialog.GetSelection()) { + processFile(entry.second); + } + } else if (demoFileDialogAction == DemoFileDialogAction::Save) { + std::cout << "Selected output file: " << demoFileDialog.GetFilePathName() << std::endl; + } else { + std::cout << "Selected directory: " << demoFileDialog.GetCurrentPath() << std::endl; + } + } + demoFileDialog.Close(); + demoFileDialogAction = DemoFileDialogAction::None; + } + } + if (ImGui::Button("run subroutine")) { // mySubroutine(); } diff --git a/include/polyscope/imgui_file_dialog_config.h b/include/polyscope/imgui_file_dialog_config.h new file mode 100644 index 00000000..04c097da --- /dev/null +++ b/include/polyscope/imgui_file_dialog_config.h @@ -0,0 +1,8 @@ +#pragma once + +#include "ImGuiFileDialogConfig.h" +#include "IconFontCppHeaders/IconsLucide.h" + +#define dirEntryString ICON_LC_FOLDER " " +#define linkEntryString ICON_LC_FILE_SYMLINK " " +#define fileEntryString ICON_LC_FILE " " diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b4eca3b..b6c86239 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -303,6 +303,7 @@ SET(HEADERS ${INCLUDE_ROOT}/group.h ${INCLUDE_ROOT}/image_quantity.h ${INCLUDE_ROOT}/imgui_config.h + ${INCLUDE_ROOT}/imgui_file_dialog_config.h ${INCLUDE_ROOT}/implicit_helpers.h ${INCLUDE_ROOT}/implicit_helpers.ipp ${INCLUDE_ROOT}/messages.h From 68623012449f4fce727d313763ce51f7c55a22b5 Mon Sep 17 00:00:00 2001 From: Oras Phongpanangam Date: Fri, 31 Jul 2026 08:51:45 -0400 Subject: [PATCH 2/5] simplify demo and enable places --- examples/demo-app/demo_app.cpp | 69 ++++++++------------ include/polyscope/imgui_file_dialog_config.h | 5 ++ 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index 075dfc17..2b911518 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -22,6 +22,7 @@ #include "ImGuiFileDialog.h" #include "IconFontCppHeaders/IconsLucide.h" +#include #include #include #include @@ -871,10 +872,14 @@ void processFile(std::string filename) { } } -enum class DemoFileDialogAction { None, OpenOne, OpenMany, Save, Directory }; +enum class DemoFileDialogAction { None, OpenMesh, Save }; -IGFD::FileDialog demoFileDialog; DemoFileDialogAction demoFileDialogAction = DemoFileDialogAction::None; +char demoSelectedFileDialogPath[4096] = ""; + +void setDemoSelectedFileDialogPath(const std::string& path) { + std::snprintf(demoSelectedFileDialogPath, sizeof(demoSelectedFileDialogPath), "%s", path.c_str()); +} void callback() { @@ -890,42 +895,25 @@ void callback() { ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("File dialogs")) { - if (ImGui::Button(ICON_LC_FOLDER_OPEN " Open mesh file")) { - IGFD::FileDialogConfig config; - config.path = "."; - config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode; - demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_FOLDER_OPEN " Open a mesh", - "Mesh files{.obj,.ply,.mesh},All files{.*}", config); - demoFileDialogAction = DemoFileDialogAction::OpenOne; - } + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::InputText("Selected path", demoSelectedFileDialogPath, sizeof(demoSelectedFileDialogPath), + ImGuiInputTextFlags_ReadOnly); - if (ImGui::Button(ICON_LC_FILES " Open multiple files")) { + if (ImGui::Button(ICON_LC_FOLDER_OPEN " Open mesh file")) { IGFD::FileDialogConfig config; - config.path = "."; - config.countSelectionMax = 0; - config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode | - ImGuiFileDialogFlags_DontShowHiddenFiles | ImGuiFileDialogFlags_CaseInsensitiveExtentionFiltering; - demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_FILES " Open mesh files", ".obj,.ply,.mesh", config); - demoFileDialogAction = DemoFileDialogAction::OpenMany; + config.flags = ImGuiFileDialogFlags_Modal; + ImGuiFileDialog::Instance()->OpenDialog("DemoFileDialog", ICON_LC_FOLDER_OPEN " Open a mesh", + "Mesh files{.obj,.ply,.mesh},All files{.*}", config); + demoFileDialogAction = DemoFileDialogAction::OpenMesh; } - if (ImGui::Button(ICON_LC_SAVE " Choose output file")) { + if (ImGui::Button(ICON_LC_SAVE " Save file")) { IGFD::FileDialogConfig config; - config.path = "."; config.fileName = "output.obj"; - config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode | - ImGuiFileDialogFlags_ConfirmOverwrite; - demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_SAVE " Choose an output file", ".obj", config); + config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_ConfirmOverwrite; + ImGuiFileDialog::Instance()->OpenDialog("DemoFileDialog", ICON_LC_SAVE " Choose an output file", ".obj", config); demoFileDialogAction = DemoFileDialogAction::Save; } - - if (ImGui::Button(ICON_LC_FOLDER " Choose directory")) { - IGFD::FileDialogConfig config; - config.path = "."; - config.flags = ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_DisableThumbnailMode; - demoFileDialog.OpenDialog("DemoFileDialog", ICON_LC_FOLDER " Choose a directory", nullptr, config); - demoFileDialogAction = DemoFileDialogAction::Directory; - } ImGui::TreePop(); } @@ -933,20 +921,19 @@ void callback() { const ImVec2 viewportSize = ImGui::GetMainViewport()->WorkSize; const ImVec2 minDialogSize(viewportSize.x * 0.5f, viewportSize.y * 0.5f); const ImVec2 maxDialogSize(viewportSize.x * 0.9f, viewportSize.y * 0.9f); - if (demoFileDialog.Display("DemoFileDialog", ImGuiWindowFlags_NoCollapse, minDialogSize, maxDialogSize)) { - if (demoFileDialog.IsOk()) { - if (demoFileDialogAction == DemoFileDialogAction::OpenOne || - demoFileDialogAction == DemoFileDialogAction::OpenMany) { - for (const auto& entry : demoFileDialog.GetSelection()) { - processFile(entry.second); - } - } else if (demoFileDialogAction == DemoFileDialogAction::Save) { - std::cout << "Selected output file: " << demoFileDialog.GetFilePathName() << std::endl; + if (ImGuiFileDialog::Instance()->Display("DemoFileDialog", ImGuiWindowFlags_NoCollapse, minDialogSize, maxDialogSize)) { + if (ImGuiFileDialog::Instance()->IsOk()) { + if (demoFileDialogAction == DemoFileDialogAction::OpenMesh) { + const std::string path = ImGuiFileDialog::Instance()->GetFilePathName(); + setDemoSelectedFileDialogPath(path); + processFile(path); } else { - std::cout << "Selected directory: " << demoFileDialog.GetCurrentPath() << std::endl; + const std::string path = ImGuiFileDialog::Instance()->GetFilePathName(); + setDemoSelectedFileDialogPath(path); + std::cout << "Selected output file: " << path << std::endl; } } - demoFileDialog.Close(); + ImGuiFileDialog::Instance()->Close(); demoFileDialogAction = DemoFileDialogAction::None; } } diff --git a/include/polyscope/imgui_file_dialog_config.h b/include/polyscope/imgui_file_dialog_config.h index 04c097da..e120a473 100644 --- a/include/polyscope/imgui_file_dialog_config.h +++ b/include/polyscope/imgui_file_dialog_config.h @@ -3,6 +3,11 @@ #include "ImGuiFileDialogConfig.h" #include "IconFontCppHeaders/IconsLucide.h" +#define USE_PLACES_FEATURE +#define PLACES_PANE_DEFAULT_SHOWN false +#define USE_PLACES_BOOKMARKS +#define USE_PLACES_DEVICES + #define dirEntryString ICON_LC_FOLDER " " #define linkEntryString ICON_LC_FILE_SYMLINK " " #define fileEntryString ICON_LC_FILE " " From 4e9b3d99c80cfdb7e2f391b93841dddfd683e112 Mon Sep 17 00:00:00 2001 From: Oras Phongpanangam Date: Fri, 31 Jul 2026 09:54:39 -0400 Subject: [PATCH 3/5] use more icons --- include/polyscope/imgui_file_dialog_config.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/polyscope/imgui_file_dialog_config.h b/include/polyscope/imgui_file_dialog_config.h index e120a473..0beef1ae 100644 --- a/include/polyscope/imgui_file_dialog_config.h +++ b/include/polyscope/imgui_file_dialog_config.h @@ -11,3 +11,12 @@ #define dirEntryString ICON_LC_FOLDER " " #define linkEntryString ICON_LC_FILE_SYMLINK " " #define fileEntryString ICON_LC_FILE " " + +#define createDirButtonString ICON_LC_FOLDER_PLUS +#define resetButtonString ICON_LC_REFRESH_CW +#define editPathButtonString ICON_LC_PENCIL + +#define addPlaceButtonString ICON_LC_BOOKMARK_PLUS +#define removePlaceButtonString ICON_LC_BOOKMARK_MINUS +#define validatePlaceButtonString ICON_LC_CHECK +#define editPlaceButtonString ICON_LC_PENCIL From e7a43718402802725b5fee4b17491b305a327755 Mon Sep 17 00:00:00 2001 From: Oras Phongpanangam Date: Fri, 31 Jul 2026 10:15:22 -0400 Subject: [PATCH 4/5] fix include ordering --- examples/demo-app/demo_app.cpp | 5 +++-- include/polyscope/imgui_file_dialog_config.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index 2b911518..b60b5e77 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -19,8 +19,8 @@ #include "polyscope/volume_grid.h" #include "polyscope/volume_mesh.h" -#include "ImGuiFileDialog.h" #include "IconFontCppHeaders/IconsLucide.h" +#include "ImGuiFileDialog.h" #include #include @@ -921,7 +921,8 @@ void callback() { const ImVec2 viewportSize = ImGui::GetMainViewport()->WorkSize; const ImVec2 minDialogSize(viewportSize.x * 0.5f, viewportSize.y * 0.5f); const ImVec2 maxDialogSize(viewportSize.x * 0.9f, viewportSize.y * 0.9f); - if (ImGuiFileDialog::Instance()->Display("DemoFileDialog", ImGuiWindowFlags_NoCollapse, minDialogSize, maxDialogSize)) { + if (ImGuiFileDialog::Instance()->Display("DemoFileDialog", ImGuiWindowFlags_NoCollapse, minDialogSize, + maxDialogSize)) { if (ImGuiFileDialog::Instance()->IsOk()) { if (demoFileDialogAction == DemoFileDialogAction::OpenMesh) { const std::string path = ImGuiFileDialog::Instance()->GetFilePathName(); diff --git a/include/polyscope/imgui_file_dialog_config.h b/include/polyscope/imgui_file_dialog_config.h index 0beef1ae..dd34deb4 100644 --- a/include/polyscope/imgui_file_dialog_config.h +++ b/include/polyscope/imgui_file_dialog_config.h @@ -1,7 +1,7 @@ #pragma once -#include "ImGuiFileDialogConfig.h" #include "IconFontCppHeaders/IconsLucide.h" +#include "ImGuiFileDialogConfig.h" #define USE_PLACES_FEATURE #define PLACES_PANE_DEFAULT_SHOWN false From f05dac8d66c85a1c1d1555e5a793cd221185a56b Mon Sep 17 00:00:00 2001 From: Oras Phongpanangam Date: Fri, 31 Jul 2026 10:46:25 -0400 Subject: [PATCH 5/5] move imguifiledialog config to reduce surface area with dependency --- deps/imgui/CMakeLists.txt | 4 ++-- .../imgui/config/polyscope_imgui_file_dialog_config.h | 0 src/CMakeLists.txt | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) rename include/polyscope/imgui_file_dialog_config.h => deps/imgui/config/polyscope_imgui_file_dialog_config.h (100%) diff --git a/deps/imgui/CMakeLists.txt b/deps/imgui/CMakeLists.txt index e061724d..51df9880 100644 --- a/deps/imgui/CMakeLists.txt +++ b/deps/imgui/CMakeLists.txt @@ -68,10 +68,10 @@ endif() add_library(imgui ${SRCS}) target_compile_features(imgui PUBLIC cxx_std_11) set_target_properties(imgui PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../include") +target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/config") target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/ImGuiFileDialog/") target_link_libraries(imgui PUBLIC IconFontCppHeaders) -target_compile_definitions(imgui PUBLIC CUSTOM_IMGUIFILEDIALOG_CONFIG="polyscope/imgui_file_dialog_config.h") +target_compile_definitions(imgui PUBLIC CUSTOM_IMGUIFILEDIALOG_CONFIG="polyscope_imgui_file_dialog_config.h") # Per-backend settings if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") diff --git a/include/polyscope/imgui_file_dialog_config.h b/deps/imgui/config/polyscope_imgui_file_dialog_config.h similarity index 100% rename from include/polyscope/imgui_file_dialog_config.h rename to deps/imgui/config/polyscope_imgui_file_dialog_config.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b6c86239..8b4eca3b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -303,7 +303,6 @@ SET(HEADERS ${INCLUDE_ROOT}/group.h ${INCLUDE_ROOT}/image_quantity.h ${INCLUDE_ROOT}/imgui_config.h - ${INCLUDE_ROOT}/imgui_file_dialog_config.h ${INCLUDE_ROOT}/implicit_helpers.h ${INCLUDE_ROOT}/implicit_helpers.ipp ${INCLUDE_ROOT}/messages.h