diff --git a/CHANGELOG.md b/CHANGELOG.md index ce980c4b417..5a144ccd703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ - Fix linker error "library limit of 65535 objects exceeded" with Ninja generator on MSVC (PR #7335) - Download tarballs instead of Git repos for "3rdparty/uvatlas" (PR #7371) - macOS x86_64 not longer supported, only macOS arm64 is supported. +- Add expand/collapse state support for TreeView items in GUI (PR #7451) ## 0.13 diff --git a/cpp/open3d/visualization/gui/TreeView.cpp b/cpp/open3d/visualization/gui/TreeView.cpp index 72fca10bd0e..821b79c1b14 100644 --- a/cpp/open3d/visualization/gui/TreeView.cpp +++ b/cpp/open3d/visualization/gui/TreeView.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -212,6 +213,7 @@ struct TreeView::Impl { std::shared_ptr cell; Item *parent = nullptr; std::list children; + std::optional expanded; }; int id_; Item root_; @@ -361,6 +363,22 @@ void TreeView::Layout(const LayoutContext &context) { // to defer layout to Draw(). } +bool TreeView::IsItemExpanded(ItemId item_id) const { + auto it = impl_->id2item_.find(item_id); + if (it == impl_->id2item_.end()) { + return false; + } + return it->second->expanded.value_or(true); +} + +void TreeView::SetItemExpanded(ItemId item_id, bool expanded) { + auto it = impl_->id2item_.find(item_id); + if (it == impl_->id2item_.end()) { + return; + } + it->second->expanded = expanded; +} + Widget::DrawResult TreeView::Draw(const DrawContext &context) { auto result = Widget::DrawResult::NONE; auto &frame = GetFrame(); @@ -417,8 +435,12 @@ Widget::DrawResult TreeView::Draw(const DrawContext &context) { colorToImguiRGBA(context.theme.tree_selected_color)); } - int flags = ImGuiTreeNodeFlags_DefaultOpen | - ImGuiTreeNodeFlags_AllowItemOverlap; + int flags = ImGuiTreeNodeFlags_AllowItemOverlap; + bool expanded = item.expanded.value_or(true); + + if (expanded) { + flags |= ImGuiTreeNodeFlags_DefaultOpen; + } if (impl_->can_select_parents_) { flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick; flags |= ImGuiTreeNodeFlags_OpenOnArrow; @@ -465,7 +487,10 @@ Widget::DrawResult TreeView::Draw(const DrawContext &context) { } }; - if (ImGui::TreeNodeEx(item.id_string.c_str(), flags, "%s", "")) { + bool open = ImGui::TreeNodeEx(item.id_string.c_str(), flags, "%s", ""); + item.expanded = open; + + if (open) { DrawThis(item, height, is_selectable); for (auto &child : item.children) { diff --git a/cpp/open3d/visualization/gui/TreeView.h b/cpp/open3d/visualization/gui/TreeView.h index 956f896149e..d19527492e7 100644 --- a/cpp/open3d/visualization/gui/TreeView.h +++ b/cpp/open3d/visualization/gui/TreeView.h @@ -134,6 +134,9 @@ class TreeView : public Widget { void SetOnSelectionChanged( std::function on_selection_changed); + bool IsItemExpanded(ItemId item_id) const; + void SetItemExpanded(ItemId item_id, bool expanded); + private: struct Impl; std::unique_ptr impl_; diff --git a/cpp/pybind/visualization/gui/gui.cpp b/cpp/pybind/visualization/gui/gui.cpp index 2d92297062f..e3f5d0c331b 100644 --- a/cpp/pybind/visualization/gui/gui.cpp +++ b/cpp/pybind/visualization/gui/gui.cpp @@ -1664,7 +1664,9 @@ void pybind_gui_definitions(py::module &m) { "The currently selected item") .def("set_on_selection_changed", &TreeView::SetOnSelectionChanged, "Sets f(new_item_id) which is called when the user " - "changes the selection."); + "changes the selection.") + .def("is_item_expanded", &TreeView::IsItemExpanded) + .def("set_item_expanded", &TreeView::SetItemExpanded); // ---- TreeView cells ---- auto checkable_cell = static_cast< diff --git a/cpp/tests/visualization/CMakeLists.txt b/cpp/tests/visualization/CMakeLists.txt index 1d216c0591f..dbee3c13870 100644 --- a/cpp/tests/visualization/CMakeLists.txt +++ b/cpp/tests/visualization/CMakeLists.txt @@ -3,3 +3,17 @@ if (BUILD_GUI) rendering/MaterialModifier.cpp ) endif() + + +add_executable(TreeViewTest + TreeViewTest.cpp +) + +target_link_libraries(TreeViewTest + PRIVATE + Open3D::Open3D +) + +set_target_properties(TreeViewTest PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) diff --git a/cpp/tests/visualization/TreeViewTest.cpp b/cpp/tests/visualization/TreeViewTest.cpp new file mode 100644 index 00000000000..88d6afa28b3 --- /dev/null +++ b/cpp/tests/visualization/TreeViewTest.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include "open3d/visualization/gui/Application.h" +#include "open3d/visualization/gui/Window.h" +#include "open3d/visualization/gui/TreeView.h" +#include "open3d/visualization/gui/Layout.h" + +using namespace open3d::visualization::gui; + +int main() { + auto& app = Application::GetInstance(); + app.Initialize(); + + auto window = std::make_shared("TreeView GUI Test", 400, 300); + + // Layout + auto layout = std::make_shared(10); // spacing = 10 + + // TreeView + auto tree = std::make_shared(); + auto root = tree->GetRootItem(); + tree->AddTextItem(root, "Item A"); + tree->AddTextItem(root, "Item B"); + + layout->AddChild(tree); + window->AddChild(layout); + + app.AddWindow(window); + + // Close automatically after 2 seconds + app.PostToMainThread(nullptr, []() { + // NOTE: + // This is a GUI smoke test. + // The window is shown briefly to ensure TreeView can be + // created, attached, and rendered without crashing. + // The application exits automatically to avoid blocking CI. + + std::this_thread::sleep_for(std::chrono::seconds(2)); + Application::GetInstance().Quit(); + }); + + app.Run(); + return 0; +} diff --git a/git b/git new file mode 100644 index 00000000000..e69de29bb2d diff --git a/treeview-clean b/treeview-clean new file mode 100644 index 00000000000..e69de29bb2d