Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
- Fix color artifacts in PointCloud projection due to CUDA race condition [(PR #7424)](https://github.com/isl-org/Open3D/pull/7424)
- Fix Windows build failure for PyTorch ops due to PyTorch's bundled fmt (v11+) requiring `/utf-8` with MSVC (PR #7447)
- Add `GetMenu` for MenuBase for easy menu item/submenu control. (PR #7295)

- Add expand/collapse state support for TreeView items in GUI (PR #7451)
## 0.13

- CUDA support 10.1 -> 11.0. Tensorflow 2.3.1 -> 2.4.1. PyTorch 1.6.0 -> 1.7.1 (PR #3049). This requires a custom PyTorch wheel from <https://github.com/isl-org/open3d_downloads/releases/tag/torch1.7.1> due to PyTorch issue #52663
Expand Down
29 changes: 26 additions & 3 deletions cpp/open3d/visualization/gui/TreeView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <cmath>
#include <list>
#include <optional>
#include <sstream>
#include <unordered_map>

Expand Down Expand Up @@ -212,6 +213,7 @@ struct TreeView::Impl {
std::shared_ptr<Widget> cell;
Item *parent = nullptr;
std::list<Item> children;
std::optional<bool> expanded;
};
int id_;
Item root_;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -417,8 +435,8 @@ Widget::DrawResult TreeView::Draw(const DrawContext &context) {
colorToImguiRGBA(context.theme.tree_selected_color));
}

int flags = ImGuiTreeNodeFlags_DefaultOpen |
ImGuiTreeNodeFlags_AllowItemOverlap;
int flags = ImGuiTreeNodeFlags_AllowItemOverlap;

if (impl_->can_select_parents_) {
flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick;
flags |= ImGuiTreeNodeFlags_OpenOnArrow;
Expand Down Expand Up @@ -465,7 +483,12 @@ Widget::DrawResult TreeView::Draw(const DrawContext &context) {
}
};

if (ImGui::TreeNodeEx(item.id_string.c_str(), flags, "%s", "")) {
ImGui::SetNextItemOpen(item.expanded.value_or(true));

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) {
Expand Down
3 changes: 3 additions & 0 deletions cpp/open3d/visualization/gui/TreeView.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ class TreeView : public Widget {
void SetOnSelectionChanged(
std::function<void(ItemId)> on_selection_changed);

bool IsItemExpanded(ItemId item_id) const;
void SetItemExpanded(ItemId item_id, bool expanded);

private:
struct Impl;
std::unique_ptr<Impl> impl_;
Expand Down
4 changes: 3 additions & 1 deletion cpp/pybind/visualization/gui/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
13 changes: 13 additions & 0 deletions cpp/tests/visualization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,17 @@ if (BUILD_GUI)
target_sources(tests PRIVATE
rendering/MaterialModifier.cpp
)

add_executable(TreeViewTest
TreeViewTest.cpp
)

target_link_libraries(TreeViewTest
PRIVATE
Open3D::Open3D
)

set_target_properties(TreeViewTest PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
endif()
45 changes: 45 additions & 0 deletions cpp/tests/visualization/TreeViewTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <thread>
#include <chrono>

#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<Window>("TreeView GUI Test", 400, 300);

// Layout
auto layout = std::make_shared<Vert>(10); // spacing = 10

// TreeView
auto tree = std::make_shared<TreeView>();
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;
}