diff --git a/deps/polyscope b/deps/polyscope index 61fc32a..bdec709 160000 --- a/deps/polyscope +++ b/deps/polyscope @@ -1 +1 @@ -Subproject commit 61fc32a3f45458a664de3f7f24fce0f3c3950a3a +Subproject commit bdec709a5eaf48f41cb1a7a527de2e065e73dc86 diff --git a/src/cpp/core.cpp b/src/cpp/core.cpp index dc3b62d..b9be858 100644 --- a/src/cpp/core.cpp +++ b/src/cpp/core.cpp @@ -9,6 +9,7 @@ #include "polyscope/affine_remapper.h" #include "polyscope/camera_parameters.h" #include "polyscope/curve_network.h" +#include "polyscope/imgui_config.h" #include "polyscope/messages.h" #include "polyscope/pick.h" #include "polyscope/point_cloud.h" @@ -143,6 +144,10 @@ PYBIND11_MODULE(polyscope_bindings, m) { m.def("set_hide_window_after_show", [](bool x) { ps::options::hideWindowAfterShow = x; }); m.def("set_warn_for_invalid_values", [](bool x) { ps::options::warnForInvalidValues = x; }); m.def("set_display_message_popups", [](bool x) { ps::options::displayMessagePopups = x; }); + m.def("set_configure_imgui_style_callback", [](std::function x) { ps::options::configureImGuiStyleCallback = x; }); + m.def("clear_configure_imgui_style_callback", []() {ps::options::configureImGuiStyleCallback = polyscope::configureImGuiStyle;}); + m.def("set_files_dropped_callback", [](std::function&)> x) { ps::state::filesDroppedCallback = x; }); + m.def("clear_files_dropped_callback", []() {ps::state::filesDroppedCallback = nullptr;}); // === Scene extents diff --git a/src/cpp/imgui.cpp b/src/cpp/imgui.cpp index 24d1f14..662feef 100644 --- a/src/cpp/imgui.cpp +++ b/src/cpp/imgui.cpp @@ -157,12 +157,109 @@ void bind_imgui_structs(py::module& m) { .def_readonly("SortDirection", &ImGuiTableColumnSortSpecs::SortDirection) ; + #define VEC2_PROPERTY(name) \ + def_property(#name, [](const ImGuiStyle& style) { \ + return from_vec2(style.name); \ + }, [](ImGuiStyle& style, const Vec2T& value) { \ + style.name = to_vec2(value); \ + }) + + + // Style + py::class_(m, "ImGuiStyle") + .def_readwrite("Alpha", &ImGuiStyle::Alpha) // float + .def_readwrite("DisabledAlpha", &ImGuiStyle::DisabledAlpha) // float + .VEC2_PROPERTY(WindowPadding) // ImVec2 + .def_readwrite("WindowRounding", &ImGuiStyle::WindowRounding) // float + .def_readwrite("WindowBorderSize", &ImGuiStyle::WindowBorderSize) // float + .def_readwrite("WindowBorderHoverPadding", &ImGuiStyle::WindowBorderHoverPadding) // float + .VEC2_PROPERTY(WindowMinSize) // ImVec2 + .VEC2_PROPERTY(WindowTitleAlign) // ImVec2 + .def_readwrite("WindowMenuButtonPosition", &ImGuiStyle::WindowMenuButtonPosition) // ImGuiDir + .def_readwrite("ChildRounding", &ImGuiStyle::ChildRounding) // float + .def_readwrite("ChildBorderSize", &ImGuiStyle::ChildBorderSize) // float + .def_readwrite("PopupRounding", &ImGuiStyle::PopupRounding) // float + .def_readwrite("PopupBorderSize", &ImGuiStyle::PopupBorderSize) // float + .VEC2_PROPERTY(FramePadding) // ImVec2 + .def_readwrite("FrameRounding", &ImGuiStyle::FrameRounding) // float + .def_readwrite("FrameBorderSize", &ImGuiStyle::FrameBorderSize) // float + .VEC2_PROPERTY(ItemSpacing) // ImVec2 + .VEC2_PROPERTY(ItemInnerSpacing) // ImVec2 + .VEC2_PROPERTY(CellPadding) // ImVec2 + .VEC2_PROPERTY(TouchExtraPadding) // ImVec2 + .def_readwrite("IndentSpacing", &ImGuiStyle::IndentSpacing) // float + .def_readwrite("ColumnsMinSpacing", &ImGuiStyle::ColumnsMinSpacing) // float + .def_readwrite("ScrollbarSize", &ImGuiStyle::ScrollbarSize) // float + .def_readwrite("ScrollbarRounding", &ImGuiStyle::ScrollbarRounding) // float + .def_readwrite("GrabMinSize", &ImGuiStyle::GrabMinSize) // float + .def_readwrite("GrabRounding", &ImGuiStyle::GrabRounding) // float + .def_readwrite("LogSliderDeadzone", &ImGuiStyle::LogSliderDeadzone) // float + .def_readwrite("ImageBorderSize", &ImGuiStyle::ImageBorderSize) // float + .def_readwrite("TabRounding", &ImGuiStyle::TabRounding) // float + .def_readwrite("TabBorderSize", &ImGuiStyle::TabBorderSize) // float + .def_readwrite("TabCloseButtonMinWidthSelected", &ImGuiStyle::TabCloseButtonMinWidthSelected) // float + .def_readwrite("TabCloseButtonMinWidthUnselected", &ImGuiStyle::TabCloseButtonMinWidthUnselected) // float + .def_readwrite("TabBarBorderSize", &ImGuiStyle::TabBarBorderSize) // float + .def_readwrite("TabBarOverlineSize", &ImGuiStyle::TabBarOverlineSize) // float + .def_readwrite("TableAngledHeadersAngle", &ImGuiStyle::TableAngledHeadersAngle) // float + .VEC2_PROPERTY(TableAngledHeadersTextAlign) // ImVec2 + .def_readwrite("ColorButtonPosition", &ImGuiStyle::ColorButtonPosition) // ImGuiDir + .VEC2_PROPERTY(ButtonTextAlign) // ImVec2 + .VEC2_PROPERTY(SelectableTextAlign) // ImVec2 + .def_readwrite("SeparatorTextBorderSize", &ImGuiStyle::SeparatorTextBorderSize) // float + .VEC2_PROPERTY(SeparatorTextAlign) // ImVec2 + .VEC2_PROPERTY(SeparatorTextPadding) // ImVec2 + .VEC2_PROPERTY(DisplayWindowPadding) // ImVec2 + .VEC2_PROPERTY(DisplaySafeAreaPadding) // ImVec2 + .def_readwrite("MouseCursorScale", &ImGuiStyle::MouseCursorScale) // float + .def_readwrite("AntiAliasedLines", &ImGuiStyle::AntiAliasedLines) // bool + .def_readwrite("AntiAliasedLinesUseTex", &ImGuiStyle::AntiAliasedLinesUseTex) // bool + .def_readwrite("AntiAliasedFill", &ImGuiStyle::AntiAliasedFill) // bool + .def_readwrite("CurveTessellationTol", &ImGuiStyle::CurveTessellationTol) // float + .def_readwrite("CircleTessellationMaxError", &ImGuiStyle::CircleTessellationMaxError) // float + + // Colors (ImVec4[ImGuiCol_COUNT]) + // Note: having explicit getter and setter functions for colors will avoid accidental no-ops such as: + // style.Colors[2] = ... + .def("GetColors", [](const ImGuiStyle &o) { + py::list colors; + for (int i = 0; i < ImGuiCol_COUNT; i++) { + colors.append(from_vec4(o.Colors[i])); + } + return colors; + }) + .def("SetColors", [](ImGuiStyle &o, const py::list& colors) { + if (colors.size() != ImGuiCol_COUNT) { + throw std::runtime_error("Expected " + std::to_string(ImGuiCol_COUNT) + + " colors, got " + std::to_string(colors.size())); + } + for (int i = 0; i < ImGuiCol_COUNT; i++) { + if (py::len(colors[i]) != 4) { + throw std::runtime_error("Expected 4 elements for color " + std::to_string(i) + + ", got " + std::to_string(py::len(colors[i]))); + } + o.Colors[i] = to_vec4(py::cast(colors[i])); + } + }, py::arg("colors")) + + // Behaviors + // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO) + .def_readwrite("HoverStationaryDelay", &ImGuiStyle::HoverStationaryDelay) // float + .def_readwrite("HoverDelayShort", &ImGuiStyle::HoverDelayShort) // float + .def_readwrite("HoverDelayNormal", &ImGuiStyle::HoverDelayNormal) // float + .def_readwrite("HoverFlagsForTooltipMouse", &ImGuiStyle::HoverFlagsForTooltipMouse) // ImGuiHoveredFlags + .def_readwrite("HoverFlagsForTooltipNav", &ImGuiStyle::HoverFlagsForTooltipNav) // ImGuiHoveredFlags + + .def("ScaleAllSizes", &ImGuiStyle::ScaleAllSizes); + + #undef VEC2_PROPERTY } void bind_imgui_methods(py::module& m) { // Main m.def("GetIO", &ImGui::GetIO, py::return_value_policy::reference); + m.def("GetStyle", &ImGui::GetStyle, py::return_value_policy::reference); // Windows m.def( @@ -495,6 +592,10 @@ void bind_imgui_methods(py::module& m) { "BulletText", [](const char* fmt) { ImGui::BulletText("%s", fmt); }, py::arg("text")); + m.def( + "SeparatorText", + [](const char* label) { ImGui::SeparatorText(label); }, + py::arg("label")); // Widgets: Main m.def( @@ -558,6 +659,42 @@ void bind_imgui_methods(py::module& m) { py::arg("size_arg") = std::make_tuple(-1.f, 0.f)); m.def("Bullet", []() { ImGui::Bullet(); }); + // Widgets: Image + m.def( + "Image", + [](ImTextureID user_texture_id, const Vec2T& image_size, const Vec2T& uv0, const Vec2T& uv1) { + ImGui::Image(user_texture_id, to_vec2(image_size), to_vec2(uv0), to_vec2(uv1)); + }, + py::arg("user_texture_id"), + py::arg("image_size"), + py::arg("uv0") = Vec2T(0.0f, 0.0f), + py::arg("uv1") = Vec2T(1.0f, 1.0f)); + m.def( + "ImageWithBg", + [](ImTextureID user_texture_id, const Vec2T& image_size, const Vec2T& uv0, const Vec2T& uv1, const Vec4T& bg_col, const Vec4T& tint_col) { + ImGui::ImageWithBg(user_texture_id, to_vec2(image_size), to_vec2(uv0), to_vec2(uv1), to_vec4(bg_col), to_vec4(tint_col)); + }, + py::arg("user_texture_id"), + py::arg("image_size"), + py::arg("uv0") = Vec2T(0.0f, 0.0f), + py::arg("uv1") = Vec2T(1.0f, 1.0f), + py::arg("bg_col") = Vec4T(0.0f, 0.0f, 0.0f, 0.0f), + py::arg("tint_col") = Vec4T(1.0f, 1.0f, 1.0f, 1.0f)); + + m.def( + "ImageButton", + [](const char* str_id, ImTextureID user_texture_id, const Vec2T& image_size, const Vec2T& uv0, const Vec2T& uv1, const Vec4T& bg_col, const Vec4T& tint_col) { + return ImGui::ImageButton(str_id, user_texture_id, to_vec2(image_size), to_vec2(uv0), to_vec2(uv1), to_vec4(bg_col), to_vec4(tint_col)); + }, + py::arg("str_id"), + py::arg("user_texture_id"), + py::arg("image_size"), + py::arg("uv0") = Vec2T(0.0f, 0.0f), + py::arg("uv1") = Vec2T(1.0f, 1.0f), + py::arg("bg_col") = Vec4T(0.0f, 0.0f, 0.0f, 0.0f), + py::arg("tint_col") = Vec4T(1.0f, 1.0f, 1.0f, 1.0f)); + + // Widgets: Combo Box m.def( "BeginCombo", @@ -2472,6 +2609,7 @@ void bind_imgui_enums(py::module& m) { m.attr("ImGuiCol_ScrollbarGrabActive") = static_cast(ImGuiCol_ScrollbarGrabActive); m.attr("ImGuiCol_CheckMark") = static_cast(ImGuiCol_CheckMark); m.attr("ImGuiCol_SliderGrab") = static_cast(ImGuiCol_SliderGrab); + m.attr("ImGuiCol_ScrollbarGrabHovered") = static_cast(ImGuiCol_ScrollbarGrabHovered); m.attr("ImGuiCol_SliderGrabActive") = static_cast(ImGuiCol_SliderGrabActive); m.attr("ImGuiCol_Button") = static_cast(ImGuiCol_Button); m.attr("ImGuiCol_ButtonHovered") = static_cast(ImGuiCol_ButtonHovered); @@ -2485,19 +2623,31 @@ void bind_imgui_enums(py::module& m) { m.attr("ImGuiCol_ResizeGrip") = static_cast(ImGuiCol_ResizeGrip); m.attr("ImGuiCol_ResizeGripHovered") = static_cast(ImGuiCol_ResizeGripHovered); m.attr("ImGuiCol_ResizeGripActive") = static_cast(ImGuiCol_ResizeGripActive); - m.attr("ImGuiCol_Tab") = static_cast(ImGuiCol_Tab); m.attr("ImGuiCol_TabHovered") = static_cast(ImGuiCol_TabHovered); - m.attr("ImGuiCol_TabActive") = static_cast(ImGuiCol_TabActive); + m.attr("ImGuiCol_Tab") = static_cast(ImGuiCol_Tab); + m.attr("ImGuiCol_TabSelected") = static_cast(ImGuiCol_TabSelected); + m.attr("ImGuiCol_TabSelectedOverline") = static_cast(ImGuiCol_TabSelectedOverline); + m.attr("ImGuiCol_TabDimmed") = static_cast(ImGuiCol_TabDimmed); + m.attr("ImGuiCol_TabDimmedSelected") = static_cast(ImGuiCol_TabDimmedSelected); + m.attr("ImGuiCol_TabDimmedSelectedOverline") = static_cast(ImGuiCol_TabDimmedSelectedOverline); + m.attr("ImGuiCol_TabActive") = static_cast(ImGuiCol_TabSelected); // Deprecated m.attr("ImGuiCol_TabUnfocused") = static_cast(ImGuiCol_TabUnfocused); m.attr("ImGuiCol_TabUnfocusedActive") = static_cast(ImGuiCol_TabUnfocusedActive); m.attr("ImGuiCol_PlotLines") = static_cast(ImGuiCol_PlotLines); m.attr("ImGuiCol_PlotLinesHovered") = static_cast(ImGuiCol_PlotLinesHovered); m.attr("ImGuiCol_PlotHistogram") = static_cast(ImGuiCol_PlotHistogram); m.attr("ImGuiCol_PlotHistogramHovered") = static_cast(ImGuiCol_PlotHistogramHovered); + m.attr("ImGuiCol_TableHeaderBg") = static_cast(ImGuiCol_TableHeaderBg); + m.attr("ImGuiCol_TableBorderStrong") = static_cast(ImGuiCol_TableBorderStrong); + m.attr("ImGuiCol_TableBorderLight") = static_cast(ImGuiCol_TableBorderLight); + m.attr("ImGuiCol_TableRowBg") = static_cast(ImGuiCol_TableRowBg); + m.attr("ImGuiCol_TableRowBgAlt") = static_cast(ImGuiCol_TableRowBgAlt); + m.attr("ImGuiCol_TextLink") = static_cast(ImGuiCol_TextLink); m.attr("ImGuiCol_TextSelectedBg") = static_cast(ImGuiCol_TextSelectedBg); m.attr("ImGuiCol_DragDropTarget") = static_cast(ImGuiCol_DragDropTarget); - m.attr("ImGuiCol_NavHighlight") = static_cast(ImGuiCol_NavHighlight); + m.attr("ImGuiCol_NavCursor") = static_cast(ImGuiCol_NavCursor); m.attr("ImGuiCol_NavWindowingHighlight") = static_cast(ImGuiCol_NavWindowingHighlight); + m.attr("ImGuiCol_NavHighlight") = static_cast(ImGuiCol_NavCursor); // Deprecated m.attr("ImGuiCol_NavWindowingDimBg") = static_cast(ImGuiCol_NavWindowingDimBg); m.attr("ImGuiCol_ModalWindowDimBg") = static_cast(ImGuiCol_ModalWindowDimBg); m.attr("ImGuiCol_COUNT") = static_cast(ImGuiCol_COUNT); @@ -2589,4 +2739,3 @@ void bind_imgui_enums(py::module& m) { m.attr("ImDrawFlags_RoundCornersAll") = static_cast(ImDrawFlags_RoundCornersAll); } - diff --git a/src/polyscope/core.py b/src/polyscope/core.py index e9cd241..c01c1fb 100644 --- a/src/polyscope/core.py +++ b/src/polyscope/core.py @@ -180,6 +180,18 @@ def set_warn_for_invalid_values(b): def set_display_message_popups(b): psb.set_display_message_popups(b) +def set_configure_imgui_style_callback(f): + return psb.set_configure_imgui_style_callback(f) + +def clear_configure_imgui_style_callback(): + return psb.clear_configure_imgui_style_callback() + +def set_files_dropped_callback(f): + return psb.set_files_dropped_callback(f) + +def clear_files_dropped_callback(): + return psb.clear_files_dropped_callback() + def set_navigation_style(s): psb.set_navigation_style(str_to_navigate_style(s)) def get_navigation_style(): diff --git a/test/polyscope_test.py b/test/polyscope_test.py index 0a36448..847daa1 100644 --- a/test/polyscope_test.py +++ b/test/polyscope_test.py @@ -121,18 +121,22 @@ def sample_callback(): ps.set_user_callback(sample_callback) ps.show(3) - - # Make sure the callback got called - self.assertEqual(3, counts[0]) - + self.assertEqual(3, counts[0]) # Make sure the callback got called ps.clear_user_callback() ps.show(3) - - # Make sure the callback didn't get called any more - self.assertEqual(3, counts[0]) - - # Make sure clearing twice is ok - ps.clear_user_callback() + self.assertEqual(3, counts[0]) # Make sure the callback didn't get called any more + ps.clear_user_callback() # Make sure clearing twice is ok + + + ## Test other callback functions + def do_nothing_callback(): + pass + ps.set_configure_imgui_style_callback(do_nothing_callback) + ps.clear_configure_imgui_style_callback() + def do_nothing_callback_2(arg): + pass + ps.set_files_dropped_callback(do_nothing_callback) + ps.clear_files_dropped_callback() def test_view_options(self):