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
30 changes: 27 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,21 @@ void TreeView::Layout(const LayoutContext &context) {
// to defer layout to Draw().
}

void TreeView::Expand(ItemId id) {
auto it = impl_->id2item_.find(id);
if (it != impl_->id2item_.end()) {
it->second->expanded = true;
}
// Invalidate();
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out Invalidate() call should either be removed or uncommented with explanation. If invalidation is needed for proper UI updates after expansion, this should be active. If not needed, the comment should be removed to avoid confusion.

Suggested change
// Invalidate();
Invalidate(); // Ensure UI is updated after expanding an item.

Copilot uses AI. Check for mistakes.
}

void TreeView::Collapse(ItemId id) {
auto it = impl_->id2item_.find(id);
if (it != impl_->id2item_.end()) {
it->second->expanded = false;
}
}

Widget::DrawResult TreeView::Draw(const DrawContext &context) {
auto result = Widget::DrawResult::NONE;
auto &frame = GetFrame();
Expand Down Expand Up @@ -417,8 +434,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;
Expand Down Expand Up @@ -465,7 +486,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) {
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);

void Expand(ItemId);
void Collapse(ItemId);

private:
struct Impl;
std::unique_ptr<Impl> impl_;
Expand Down
7 changes: 4 additions & 3 deletions cpp/pybind/t/geometry/pointcloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,10 @@ void pybind_pointcloud_definitions(py::module& m) {
"non-negative number less than number of points in the "
"input pointcloud.",
"start_index"_a = 0);
pointcloud.def("remove_radius_outliers", &PointCloud::RemoveRadiusOutliers,
"nb_points"_a, "search_radius"_a,
R"(Remove points that have less than nb_points neighbors in a
pointcloud.def(
"remove_radius_outliers", &PointCloud::RemoveRadiusOutliers,
"nb_points"_a, "search_radius"_a,
R"(Remove points that have less than nb_points neighbors in a
sphere of a given search radius.

Args:
Expand Down
8 changes: 6 additions & 2 deletions cpp/pybind/visualization/gui/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1665,9 +1665,13 @@ void pybind_gui_definitions(py::module &m) {
.def("set_on_selection_changed", &TreeView::SetOnSelectionChanged,
"Sets f(new_item_id) which is called when the user "
"changes the selection.");
.def("expand", &TreeView::Expand, "Expand (open) the given TreeView item.")

// ---- TreeView cells ----
auto checkable_cell = static_cast<
.def("collapse", &TreeView::Collapse,
"Collapse (close) the given TreeView item.")

// ---- TreeView cells ----
auto checkable_cell = static_cast<
py::class_<CheckableTextTreeCell,
UnownedPointer<CheckableTextTreeCell>, Widget>>(
m_gui.attr("CheckableTextTreeCell"));
Expand Down
14 changes: 14 additions & 0 deletions cpp/tests/visualization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,17 @@ if (BUILD_GUI)
rendering/MaterialModifier.cpp
)
endif()


add_executable(TreeViewTest
TreeViewTest.cpp
)
Comment on lines +8 to +10
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TreeViewTest is configured as a standalone executable rather than integrated with the test framework. Consider using the existing test infrastructure (similar to the rendering tests guarded by BUILD_GUI) to ensure this test runs as part of the automated test suite and reports results properly.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


target_link_libraries(TreeViewTest
PRIVATE
Open3D::Open3D
)

set_target_properties(TreeViewTest PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)