From edd0405d51e7591e00aa002151d5a3231d768afe Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Thu, 9 Oct 2025 17:36:45 +0200 Subject: [PATCH 1/9] Change behavior when object is clicked, now selection is displayed on the right --- SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp | 26 ++- SofaImGui/src/SofaImGui/ImGuiGUIEngine.h | 3 +- .../src/SofaImGui/windows/SceneGraph.cpp | 203 ++++++++++-------- SofaImGui/src/SofaImGui/windows/SceneGraph.h | 19 +- 4 files changed, 149 insertions(+), 102 deletions(-) diff --git a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp index 56c54372ed..f5f0efa740 100644 --- a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp +++ b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp @@ -91,6 +91,7 @@ namespace sofaimgui ImGuiGUIEngine::ImGuiGUIEngine() : winManagerProfiler(helper::system::FileSystem::append(sofaimgui::getConfigurationFolderPath(), std::string("profiler.txt"))) , winManagerSceneGraph(helper::system::FileSystem::append(sofaimgui::getConfigurationFolderPath(), std::string("scenegraph.txt"))) + , winManagerSelectionDescription(helper::system::FileSystem::append(sofaimgui::getConfigurationFolderPath(), std::string("selectiondescription.txt"))) , winManagerPerformances(helper::system::FileSystem::append(sofaimgui::getConfigurationFolderPath(), std::string("performances.txt"))) , winManagerDisplayFlags(helper::system::FileSystem::append(sofaimgui::getConfigurationFolderPath(), std::string("displayflags.txt"))) , winManagerPlugins(helper::system::FileSystem::append(sofaimgui::getConfigurationFolderPath(), std::string("plugins.txt"))) @@ -360,6 +361,7 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) static constexpr auto windowNamePerformances = ICON_FA_CHART_LINE " Performances"; static constexpr auto windowNameProfiler = ICON_FA_HOURGLASS " Profiler"; static constexpr auto windowNameSceneGraph = ICON_FA_SITEMAP " Scene Graph"; + static constexpr auto windowNameSelectionDescription = ICON_FA_SITEMAP " Selection details"; static constexpr auto windowNameDisplayFlags = ICON_FA_EYE " Display Flags" ; static constexpr auto windowNamePlugins = ICON_FA_CIRCLE_PLUS " Plugins"; static constexpr auto windowNameComponents = ICON_FA_LIST " Components"; @@ -369,7 +371,7 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) if (!*firstRunState.getStatePtr()) { - resetView(dockspace_id, windowNameSceneGraph, windowNameLog, windowNameViewport); + resetView(dockspace_id, windowNameSceneGraph, windowNameSelectionDescription, windowNameLog, windowNameViewport); } ImGui::End(); @@ -564,7 +566,7 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) ImGui::Separator(); if (ImGui::MenuItem(ICON_FA_ARROWS_ROTATE " Reset UI Layout")) { - resetView(dockspace_id,windowNameSceneGraph,windowNameLog,windowNameViewport); + resetView(dockspace_id,windowNameSceneGraph,windowNameSelectionDescription, windowNameLog,windowNameViewport); } ImGui::EndMenu(); } @@ -577,6 +579,8 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) ImGui::Checkbox(windowNameSceneGraph, winManagerSceneGraph.getStatePtr()); + ImGui::Checkbox(windowNameSelectionDescription, winManagerSelectionDescription.getStatePtr()); + ImGui::Checkbox(windowNameDisplayFlags, winManagerDisplayFlags.getStatePtr()); ImGui::Checkbox(windowNamePlugins, winManagerPlugins.getStatePtr()); @@ -661,7 +665,7 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) if (m_imguiNeedViewReset) { - resetView(dockspace_id, windowNameSceneGraph, windowNameLog, windowNameViewport); + resetView(dockspace_id, windowNameSceneGraph, windowNameSelectionDescription, windowNameLog, windowNameViewport); m_imguiNeedViewReset = false; } @@ -696,13 +700,19 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) static std::set currentSelection; windows::showSceneGraph(groot, windowNameSceneGraph, openedComponents, focusedComponents, currentSelection, - winManagerSceneGraph); + winManagerSceneGraph, winManagerSelectionDescription); std::set currentSelectionV; for(auto component : currentSelection) currentSelectionV.insert(component); baseGUI->setCurrentSelection(currentSelectionV); + /*************************************** + * ShowSelection + **************************************/ + windows::showSelection(groot, windowNameSelectionDescription, currentSelection, focusedComponents, + winManagerSelectionDescription); + /*************************************** * Display flags window **************************************/ @@ -762,7 +772,7 @@ void ImGuiGUIEngine::endFrame() std::setlocale(LC_NUMERIC, m_localeBackup.c_str()); } -void ImGuiGUIEngine::resetView(ImGuiID dockspace_id, const char* windowNameSceneGraph, const char *windowNameLog, const char *windowNameViewport) +void ImGuiGUIEngine::resetView(ImGuiID dockspace_id, const char* windowNameSceneGraph, const char* winNameSelectionDescription, const char *windowNameLog, const char *windowNameViewport) { ImGuiViewport* viewport = ImGui::GetMainViewport(); @@ -770,16 +780,18 @@ void ImGuiGUIEngine::resetView(ImGuiID dockspace_id, const char* windowNameScene ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingInCentralNode | ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size); + auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.4f, nullptr, &dockspace_id); + ImGui::DockBuilderDockWindow(windowNameSceneGraph, dock_id_left); auto dock_id_right = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.4f, nullptr, &dockspace_id); - ImGui::DockBuilderDockWindow(windowNameSceneGraph, dock_id_right); + ImGui::DockBuilderDockWindow(winNameSelectionDescription, dock_id_right); auto dock_id_down = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Down, 0.3f, nullptr, &dockspace_id); ImGui::DockBuilderDockWindow(windowNameLog, dock_id_down); ImGui::DockBuilderDockWindow(windowNameViewport, dockspace_id); ImGui::DockBuilderFinish(dockspace_id); - winManagerViewPort.setState(true); winManagerSceneGraph.setState(true); winManagerLog.setState(true); + winManagerSelectionDescription.setState(false); firstRunState.setState(true);// Mark first run as complete } diff --git a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.h b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.h index f6a5a7a96e..81c39cad8e 100644 --- a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.h +++ b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.h @@ -75,11 +75,12 @@ class ImGuiGUIEngine : public sofaglfw::BaseGUIEngine bool isMouseOnViewport { false }; CSimpleIniA ini; void loadFile(sofaglfw::SofaGLFWBaseGUI* baseGUI, sofa::core::sptr& groot, std::string filePathName, bool reload = false); - void resetView(ImGuiID dockspace_id, const char *windowNameSceneGraph, const char *windowNameLog, const char *windowNameViewport) ; + void resetView(ImGuiID dockspace_id, const char *windowNameSceneGraph, const char *winNameSelectionDescription, const char *windowNameLog, const char *windowNameViewport) ; // WindowState members windows::WindowState winManagerProfiler; windows::WindowState winManagerSceneGraph; + windows::WindowState winManagerSelectionDescription; windows::WindowState winManagerPerformances; windows::WindowState winManagerDisplayFlags; windows::WindowState winManagerPlugins; diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index a3ee3c1e5c..17eba87557 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -47,7 +47,7 @@ namespace windows std::set& openedComponents, std::set& focusedComponents, std::set& currentSelection, - WindowState& winManagerSceneGraph) + WindowState& winManagerSceneGraph, WindowState& winManagerSelectionDescription) { std::set componentToOpen; if (*winManagerSceneGraph.getStatePtr()) @@ -75,7 +75,7 @@ namespace windows std::function showNode; showNode = [&showNode, &treeDepth, expand, collapse, &openedComponents, - &componentToOpen, ¤tSelection](sofa::simulation::Node* node) + &componentToOpen, ¤tSelection, &winManagerSelectionDescription](sofa::simulation::Node* node) { if (node == nullptr) return; if (treeDepth == 0) @@ -171,9 +171,11 @@ namespace windows if(!currentSelection.contains(clickedObject)){ currentSelection.clear(); currentSelection.insert(clickedObject); + winManagerSelectionDescription.setState(true); } else{ currentSelection.erase(clickedObject); + winManagerSelectionDescription.setState(false); } } } @@ -262,98 +264,7 @@ namespace windows ImGui::EndTable(); } - static bool areDataDisplayed; - areDataDisplayed = clickedObject != nullptr; - if (clickedObject != nullptr) - { - ImGui::Separator(); - ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); - if (ImGui::CollapsingHeader((ICON_FA_CUBE " " + clickedObject->getName()).c_str(), &areDataDisplayed)) - { - ImGui::Indent(); - std::map > groupMap; - for (auto* data : clickedObject->getDataFields()) - { - groupMap[data->getGroup()].push_back(data); - } - for (auto& [group, datas] : groupMap) - { - const auto groupName = group.empty() ? "Property" : group; - ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); - if (ImGui::CollapsingHeader(groupName.c_str())) - { - ImGui::Indent(); - for (auto& data : datas) - { - const bool isOpen = ImGui::CollapsingHeader(data->m_name.c_str()); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::TextDisabled(data->getHelp().c_str()); - ImGui::TextDisabled("Type: %s", data->getValueTypeString().c_str()); - ImGui::EndTooltip(); - } - if (isOpen) - { - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); - ImGui::TextWrapped(data->getHelp().c_str()); - - if (data->getParent()) - { - const auto linkPath = data->getLinkPath(); - if (!linkPath.empty()) - { - ImGui::TextWrapped(linkPath.c_str()); - if (ImGui::IsItemHovered()) - { - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - } - if (ImGui::IsItemClicked()) - { - auto* owner = dynamic_cast(data->getParent()->getOwner()); - focusedComponents.insert(owner); - } - } - } - - ImGui::PopStyleColor(); - sofaimgui::showWidget(*data); - } - } - ImGui::Unindent(); - } - } - ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); - if (ImGui::CollapsingHeader("Links")) - { - ImGui::Indent(); - for (const auto* link : clickedObject->getLinks()) - { - const auto linkValue = link->getValueString(); - const auto linkTitle = link->getName(); - const bool isOpen = ImGui::CollapsingHeader(linkTitle.c_str()); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::TextDisabled(link->getHelp().c_str()); - ImGui::EndTooltip(); - } - if (isOpen) - { - ImGui::TextDisabled(link->getHelp().c_str()); - ImGui::TextWrapped(linkValue.c_str()); - } - } - ImGui::Unindent(); - } - ImGui::Unindent(); - } - if (!areDataDisplayed) - { - clickedObject = nullptr; - } - } } ImGui::End(); } @@ -550,5 +461,111 @@ namespace windows } } + void showSelection(sofa::core::sptr groot, + const char* const& windowNameSelectionDescription, + std::set& currentSelection, + std::set& focusedComponents, + WindowState& winSelectionDescription) + { + if (*winSelectionDescription.getStatePtr()) + { + if (ImGui::Begin(windowNameSelectionDescription, winSelectionDescription.getStatePtr())) + { + if (currentSelection.size() > 0) + { + sofa::core::objectmodel::Base* clickedObject = (*currentSelection.begin()); + if (clickedObject != nullptr) + { + ImGui::Separator(); + ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); + std::map > groupMap; + for (auto* data : clickedObject->getDataFields()) + { + groupMap[data->getGroup()].push_back(data); + } + for (auto& [group, datas] : groupMap) + { + const auto groupName = group.empty() ? "Property" : group; + ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); + if (ImGui::CollapsingHeader(groupName.c_str())) + { + ImGui::Indent(); + for (auto& data : datas) + { + const bool isOpen = ImGui::CollapsingHeader(data->m_name.c_str()); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::TextDisabled(data->getHelp().c_str()); + ImGui::TextDisabled("Type: %s", data->getValueTypeString().c_str()); + ImGui::EndTooltip(); + } + if (isOpen) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); + ImGui::TextWrapped(data->getHelp().c_str()); + + if (data->getParent()) + { + const auto linkPath = data->getLinkPath(); + if (!linkPath.empty()) + { + ImGui::TextWrapped(linkPath.c_str()); + if (ImGui::IsItemHovered()) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + } + if (ImGui::IsItemClicked()) + { + auto* owner = dynamic_cast(data->getParent()->getOwner()); + focusedComponents.insert(owner); + } + } + } + + ImGui::PopStyleColor(); + sofaimgui::showWidget(*data); + } + } + ImGui::Unindent(); + } + } + ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); + if (ImGui::CollapsingHeader("Links")) + { + ImGui::Indent(); + for (const auto* link : clickedObject->getLinks()) + { + const auto linkValue = link->getValueString(); + const auto linkTitle = link->getName(); + + const bool isOpen = ImGui::CollapsingHeader(linkTitle.c_str()); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::TextDisabled(link->getHelp().c_str()); + ImGui::EndTooltip(); + } + if (isOpen) + { + ImGui::TextDisabled(link->getHelp().c_str()); + ImGui::TextWrapped(linkValue.c_str()); + } + } + ImGui::Unindent(); + } + } + } + else + { + ImGui::TextWrapped("Please select an object to show its details here"); + } + } + ImGui::End(); + } + + + } + } diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.h b/SofaImGui/src/SofaImGui/windows/SceneGraph.h index 9daf7c9781..627fbc92c5 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.h +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.h @@ -43,7 +43,24 @@ namespace windows std::set& openedComponents, std::set& focusedComponents, std::set& currentSelection, - WindowState& winManagerSceneGraph); + WindowState& winManagerSceneGraph, WindowState& winManagerSelectionDescription); + + + /** + * @brief Shows the Scene Graph window. + * + * This function displays the hierarchy of nodes and objects in the scene graph, allowing users to interact with and inspect different components and their properties. + * + * @param groot The root node of the scene graph. + * @param windowNameSelectionDescription The name of the Selection Description window. + * @param currentSelection A set containing pointers to the components that are currently selected. + * @param winSelectionDescription An object that contains information on the drawn window. + */ + void showSelection(sofa::core::sptr groot, + const char* const& windowNameSelectionDescription, + std::set& currentSelection, + std::set& focusedComponents, + WindowState& winSelectionDescription); } // namespace sofaimgui From 723d180900950d84bb2947555fef0326a489a91c Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Thu, 9 Oct 2025 18:05:01 +0200 Subject: [PATCH 2/9] Remove panel at open and fix layout --- SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp | 4 ++-- SofaImGui/src/SofaImGui/windows/SceneGraph.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp index f5f0efa740..36dd0cf034 100644 --- a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp +++ b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp @@ -153,6 +153,8 @@ void ImGuiGUIEngine::init() sofa::helper::system::PluginManager::getInstance().readFromIniFile( sofa::gui::common::BaseGUI::getConfigDirectoryPath() + "/loadedPlugins.ini"); + winManagerSelectionDescription.setState(false); + } void ImGuiGUIEngine::initBackend(GLFWwindow* glfwWindow) @@ -579,8 +581,6 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) ImGui::Checkbox(windowNameSceneGraph, winManagerSceneGraph.getStatePtr()); - ImGui::Checkbox(windowNameSelectionDescription, winManagerSelectionDescription.getStatePtr()); - ImGui::Checkbox(windowNameDisplayFlags, winManagerDisplayFlags.getStatePtr()); ImGui::Checkbox(windowNamePlugins, winManagerPlugins.getStatePtr()); diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index 17eba87557..ba6cd2af18 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -251,8 +251,7 @@ namespace windows static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; - ImVec2 outer_size = ImVec2(0.0f, static_cast(clickedObject) * ImGui::GetTextLineHeightWithSpacing() * 20); - if (ImGui::BeginTable("sceneGraphTable", 2, flags, outer_size)) + if (ImGui::BeginTable("sceneGraphTable", 2, flags )) { ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); @@ -476,6 +475,8 @@ namespace windows sofa::core::objectmodel::Base* clickedObject = (*currentSelection.begin()); if (clickedObject != nullptr) { + ImGui::TextWrapped((ICON_FA_CUBE " " + clickedObject->getName()).c_str()); + ImGui::Separator(); ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); std::map > groupMap; From 2d52e23a00f3ebe585ce9121e67ac69cf4faa15a Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Thu, 9 Oct 2025 18:41:30 +0200 Subject: [PATCH 3/9] Deselect when close --- SofaImGui/src/SofaImGui/windows/SceneGraph.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index ba6cd2af18..76223f444e 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -564,6 +564,10 @@ namespace windows } ImGui::End(); } + else + { + currentSelection.clear(); + } } From f5e50fc03e836acfb7f09abf9ea62ada21055908 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 10 Oct 2025 10:01:49 +0200 Subject: [PATCH 4/9] MAke it work for slaves --- .../src/SofaImGui/windows/SceneGraph.cpp | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index 76223f444e..8f4e591db2 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -208,12 +208,8 @@ namespace windows const auto& slaveName = slave->getName(); const auto slaveClassName = slave->getClassName(); const bool isSlaveHighlighted = !filter.Filters.empty() && (filter.PassFilter(slaveName.c_str()) || filter.PassFilter(slaveClassName.c_str())); - if (isSlaveHighlighted) - { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); - } - ImGui::TreeNodeEx(std::string(ICON_FA_CUBE " " + slave->getName()).c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::TreeNodeEx(std::string(ICON_FA_CUBE " ").c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); if (ImGui::IsItemClicked()) { if (ImGui::IsMouseDoubleClicked(0)) @@ -224,15 +220,33 @@ namespace windows else { clickedObject = slave.get(); + if(!currentSelection.contains(clickedObject)){ + currentSelection.clear(); + currentSelection.insert(clickedObject); + winManagerSelectionDescription.setState(true); + } + else{ + currentSelection.erase(clickedObject); + winManagerSelectionDescription.setState(false); + } } } + ImGui::SameLine(); + + doHighLight = isSlaveHighlighted || currentSelection.contains(slave.get()); + if (doHighLight) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); + } + ImGui::Text(slave->getName().c_str()); ImGui::TableNextColumn(); ImGui::TextDisabled(slave->getClassName().c_str()); - if (isSlaveHighlighted) + if (doHighLight) { ImGui::PopStyleColor(); } + ImGui::PopID(); } ImGui::TreePop(); From 4b89787c4a8d71138f5e9ff2ea003ae18b9c51aa Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 10 Oct 2025 12:06:50 +0200 Subject: [PATCH 5/9] Enable single click on node label for inspection --- .../src/SofaImGui/windows/SceneGraph.cpp | 88 +++++++++++++------ 1 file changed, 60 insertions(+), 28 deletions(-) diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index 8f4e591db2..3ed36e9d50 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -71,11 +71,12 @@ namespace windows } unsigned int treeDepth {}; - static sofa::core::objectmodel::Base* clickedObject { nullptr }; + static sofa::core::objectmodel::Base* clickedObject; + clickedObject = nullptr ; std::function showNode; - showNode = [&showNode, &treeDepth, expand, collapse, &openedComponents, - &componentToOpen, ¤tSelection, &winManagerSelectionDescription](sofa::simulation::Node* node) + showNode = [&showNode, &treeDepth, expand, collapse, + &componentToOpen, ¤tSelection](sofa::simulation::Node* node) { if (node == nullptr) return; if (treeDepth == 0) @@ -89,20 +90,54 @@ namespace windows const auto& nodeName = node->getName(); const bool isNodeHighlighted = !filter.Filters.empty() && filter.PassFilter(nodeName.c_str()); - if (isNodeHighlighted) + + ImGui::PushID(node); + + const bool open = ImGui::TreeNode(std::string(ICON_FA_CUBES " " ).c_str()); + + ImGui::PopID(); + ImGui::PushID(&nodeName); + + ImGui::SameLine(); + auto XPos = ImGui::GetCursorPosX(); + ImGui::TreeNodeEx("",ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf); + if (ImGui::IsItemClicked()) + { + if (ImGui::IsMouseDoubleClicked(0)) + { + componentToOpen.insert(dynamic_cast(node)); + clickedObject = nullptr; + } + else + { + clickedObject = node; + } + } + + ImGui::SameLine(); + + bool doHighLight = isNodeHighlighted || ((clickedObject == node) != currentSelection.contains(node)); + if (doHighLight) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); } - const bool open = ImGui::TreeNode(std::string(ICON_FA_CUBES " " + nodeName).c_str()); + ImGui::SetCursorPosX(XPos); + + ImGui::Text(node->getName().c_str()); + if (doHighLight) + { + ImGui::PopStyleColor(); + } + ImGui::TableNextColumn(); ImGui::TextDisabled("Node"); if (isNodeHighlighted) { ImGui::PopStyleColor(); } - if (ImGui::IsItemClicked()) - clickedObject = node; + ImGui::PopID(); + if (open) { for (const auto object : node->getNodeObjects()) @@ -168,21 +203,12 @@ namespace windows else { clickedObject = object; - if(!currentSelection.contains(clickedObject)){ - currentSelection.clear(); - currentSelection.insert(clickedObject); - winManagerSelectionDescription.setState(true); - } - else{ - currentSelection.erase(clickedObject); - winManagerSelectionDescription.setState(false); - } } } ImGui::SameLine(); - bool doHighLight = isObjectHighlighted || currentSelection.contains(object); + doHighLight = isObjectHighlighted || ((clickedObject == object) != currentSelection.contains(object)); if (doHighLight) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); @@ -220,20 +246,11 @@ namespace windows else { clickedObject = slave.get(); - if(!currentSelection.contains(clickedObject)){ - currentSelection.clear(); - currentSelection.insert(clickedObject); - winManagerSelectionDescription.setState(true); - } - else{ - currentSelection.erase(clickedObject); - winManagerSelectionDescription.setState(false); - } } } ImGui::SameLine(); - doHighLight = isSlaveHighlighted || currentSelection.contains(slave.get()); + doHighLight = isSlaveHighlighted || ((clickedObject == slave.get()) != currentSelection.contains(slave.get())); if (doHighLight) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); @@ -277,11 +294,26 @@ namespace windows ImGui::EndTable(); } - + if (clickedObject) + { + if(!currentSelection.contains(clickedObject)){ + currentSelection.clear(); + currentSelection.insert(clickedObject); + winManagerSelectionDescription.setState(true); + } + else + { + currentSelection.erase(clickedObject); + winManagerSelectionDescription.setState(false); + } + } } ImGui::End(); + } + + openedComponents.insert(componentToOpen.begin(), componentToOpen.end()); openedComponents.insert(focusedComponents.begin(), focusedComponents.end()); From 8fea96c10d8667ee390d2e1fbe554756a988768c Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 10 Oct 2025 15:55:07 +0200 Subject: [PATCH 6/9] Refacto drawing method to factorize selectable item in tree to get same behavior for objects with slaves as with Nodes --- SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp | 2 +- SofaImGui/src/SofaImGui/ObjectColor.cpp | 2 +- SofaImGui/src/SofaImGui/ObjectColor.h | 3 +- .../src/SofaImGui/windows/SceneGraph.cpp | 271 +++++++++--------- SofaImGui/src/SofaImGui/windows/SceneGraph.h | 7 +- 5 files changed, 140 insertions(+), 145 deletions(-) diff --git a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp index 36dd0cf034..ab0c1cf1a5 100644 --- a/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp +++ b/SofaImGui/src/SofaImGui/ImGuiGUIEngine.cpp @@ -695,7 +695,7 @@ void ImGuiGUIEngine::startFrame(sofaglfw::SofaGLFWBaseGUI* baseGUI) /*************************************** * Scene graph window **************************************/ - static std::set openedComponents; + static std::set openedComponents; static std::set focusedComponents; static std::set currentSelection; windows::showSceneGraph(groot, windowNameSceneGraph, openedComponents, diff --git a/SofaImGui/src/SofaImGui/ObjectColor.cpp b/SofaImGui/src/SofaImGui/ObjectColor.cpp index 2eda3ce444..dd9a274703 100644 --- a/SofaImGui/src/SofaImGui/ObjectColor.cpp +++ b/SofaImGui/src/SofaImGui/ObjectColor.cpp @@ -27,7 +27,7 @@ namespace sofaimgui { - ImVec4 getObjectColor(sofa::core::objectmodel::BaseObject* object) + ImVec4 getObjectColor(sofa::core::objectmodel::Base* object) { unsigned int objectType=sofa::simulation::Colors::OBJECT; if(object->toContextObject()) diff --git a/SofaImGui/src/SofaImGui/ObjectColor.h b/SofaImGui/src/SofaImGui/ObjectColor.h index 9aba77c28e..0a5d18b38c 100644 --- a/SofaImGui/src/SofaImGui/ObjectColor.h +++ b/SofaImGui/src/SofaImGui/ObjectColor.h @@ -22,6 +22,7 @@ #pragma once #include +#include namespace sofa::core::objectmodel { @@ -31,6 +32,6 @@ namespace sofa::core::objectmodel namespace sofaimgui { - ImVec4 getObjectColor(sofa::core::objectmodel::BaseObject* object); + ImVec4 getObjectColor(sofa::core::objectmodel::Base* object); } //namespace sofaimgui diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index 3ed36e9d50..aa64743eef 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -39,17 +39,124 @@ #include #include "SceneGraph.h" +#include +#include + namespace windows { + bool drawExpandableObject(sofa::core::objectmodel::Base * obj, bool isNodeHighlighted, const char* icon, const ImVec4 objectColor, std::set& componentToOpen, const std::set& currentSelection, sofa::core::objectmodel::Base* &clickedObject) + { + const auto& objName = obj->getName(); + + //Tree expand drawing (tick + icon) + ImGui::PushID(obj); + const bool open = ImGui::TreeNode((std::string(icon) + std::string(" ")) .c_str()); + ImGui::PopID(); + ImGui::PushStyleColor(ImGuiCol_Text, objectColor); + + //Name drawing to be able to select the node for inspection + ImGui::PushID(&objName); + ImGui::SameLine(); + auto XPos = ImGui::GetCursorPosX(); + //We don't write anything to just get the "clickable" space, the name is writent after we know if it is clicked or not + ImGui::TreeNodeEx("",ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf); + if (ImGui::IsItemClicked()) + { + if (ImGui::IsMouseDoubleClicked(0)) + { + componentToOpen.insert(obj); + clickedObject = nullptr; + } + else + { + clickedObject = obj; + } + } + + ImGui::SameLine(); + //Now actually write the name + bool doHighLight = isNodeHighlighted || ((clickedObject == obj) != currentSelection.contains(obj)); + if (doHighLight) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); + } + + ImGui::SetCursorPosX(XPos); + + ImGui::Text(obj->getName().c_str()); + if (doHighLight) + { + ImGui::PopStyleColor(); + } + + ImGui::TableNextColumn(); + ImGui::TextDisabled(obj->getClassName().c_str()); + if (isNodeHighlighted) + { + ImGui::PopStyleColor(); + } + ImGui::PopStyleColor(); + + ImGui::PopID(); + return open; + } + + bool drawNonExpandableObject(sofa::core::objectmodel::Base * obj, bool isObjectHighlighted, const char* icon, const ImVec4 objectColor, std::set& componentToOpen, const std::set& currentSelection, sofa::core::objectmodel::Base* &clickedObject) + { + ImGui::PushID(obj); + + ImGuiTreeNodeFlags objectFlags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf;; + + const auto& objectName = obj->getName(); + const auto objectClassName = obj->getClassName(); + + ImGui::PushStyleColor(ImGuiCol_Text, objectColor); + + const auto objectOpen = ImGui::TreeNodeEx(icon, objectFlags); + ImGui::PopStyleColor(); + + if (ImGui::IsItemClicked()) + { + if (ImGui::IsMouseDoubleClicked(0)) + { + componentToOpen.insert(obj); + clickedObject = nullptr; + } + else + { + clickedObject = obj; + } + } + + ImGui::SameLine(); + + bool doHighLight = isObjectHighlighted || ((clickedObject == obj) != currentSelection.contains(obj)); + if (doHighLight) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); + } + ImGui::Text(objectName.c_str()); + ImGui::TableNextColumn(); + ImGui::TextDisabled(objectClassName.c_str()); + ImGui::PopID(); + + if (doHighLight) + { + ImGui::PopStyleColor(); + } + + return objectOpen; + } + void showSceneGraph(sofa::core::sptr groot, const char* const& windowNameSceneGraph, - std::set& openedComponents, + std::set& openedComponents, std::set& focusedComponents, std::set& currentSelection, WindowState& winManagerSceneGraph, WindowState& winManagerSelectionDescription) { - std::set componentToOpen; + std::set componentToOpen; if (*winManagerSceneGraph.getStatePtr()) { if (ImGui::Begin(windowNameSceneGraph, winManagerSceneGraph.getStatePtr())) @@ -71,12 +178,11 @@ namespace windows } unsigned int treeDepth {}; - static sofa::core::objectmodel::Base* clickedObject; - clickedObject = nullptr ; + sofa::core::objectmodel::Base* clickedObject = nullptr ; std::function showNode; showNode = [&showNode, &treeDepth, expand, collapse, - &componentToOpen, ¤tSelection](sofa::simulation::Node* node) + &componentToOpen, ¤tSelection, &clickedObject](sofa::simulation::Node* node) { if (node == nullptr) return; if (treeDepth == 0) @@ -88,55 +194,9 @@ namespace windows ImGui::TableNextRow(); ImGui::TableNextColumn(); - const auto& nodeName = node->getName(); - const bool isNodeHighlighted = !filter.Filters.empty() && filter.PassFilter(nodeName.c_str()); - - ImGui::PushID(node); - - const bool open = ImGui::TreeNode(std::string(ICON_FA_CUBES " " ).c_str()); - - ImGui::PopID(); - ImGui::PushID(&nodeName); - - ImGui::SameLine(); - auto XPos = ImGui::GetCursorPosX(); - ImGui::TreeNodeEx("",ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf); - if (ImGui::IsItemClicked()) - { - if (ImGui::IsMouseDoubleClicked(0)) - { - componentToOpen.insert(dynamic_cast(node)); - clickedObject = nullptr; - } - else - { - clickedObject = node; - } - } - - ImGui::SameLine(); - - bool doHighLight = isNodeHighlighted || ((clickedObject == node) != currentSelection.contains(node)); - if (doHighLight) - { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); - } - - ImGui::SetCursorPosX(XPos); - - ImGui::Text(node->getName().c_str()); - if (doHighLight) - { - ImGui::PopStyleColor(); - } - - ImGui::TableNextColumn(); - ImGui::TextDisabled("Node"); - if (isNodeHighlighted) - { - ImGui::PopStyleColor(); - } - ImGui::PopID(); + ////Label and tree expand drawing + const bool isNodeHighlighted = !filter.Filters.empty() && filter.PassFilter(node->getName().c_str()); + bool open = drawExpandableObject(node, isNodeHighlighted, ICON_FA_CUBES, ImVec4(1,1,1,1), componentToOpen, currentSelection, clickedObject); if (open) { @@ -144,26 +204,6 @@ namespace windows { ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::PushID(object); - - ImGuiTreeNodeFlags objectFlags = ImGuiTreeNodeFlags_SpanFullWidth; - - const auto& slaves = object->getSlaves(); - if (slaves.empty()) - { - objectFlags |= ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf; - } - else - { - if (expand) - ImGui::SetNextItemOpen(true); - if (collapse) - ImGui::SetNextItemOpen(false); - } - - const auto& objectName = object->getName(); - const auto objectClassName = object->getClassName(); - const bool isObjectHighlighted = !filter.Filters.empty() && (filter.PassFilter(objectName.c_str()) || filter.PassFilter(objectClassName.c_str())); ImVec4 objectColor; @@ -188,40 +228,22 @@ namespace windows objectColor = sofaimgui::getObjectColor(object); } - ImGui::PushStyleColor(ImGuiCol_Text, objectColor); - - const auto objectOpen = ImGui::TreeNodeEx(icon, objectFlags); - ImGui::PopStyleColor(); - - if (ImGui::IsItemClicked()) + const auto& slaves = object->getSlaves(); + if (!slaves.empty()) { - if (ImGui::IsMouseDoubleClicked(0)) - { - componentToOpen.insert(object); - clickedObject = nullptr; - } - else - { - clickedObject = object; - } + if (expand) + ImGui::SetNextItemOpen(true); + if (collapse) + ImGui::SetNextItemOpen(false); } - ImGui::SameLine(); - - doHighLight = isObjectHighlighted || ((clickedObject == object) != currentSelection.contains(object)); - if (doHighLight) - { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); - } - ImGui::Text(object->getName().c_str()); - ImGui::TableNextColumn(); - ImGui::TextDisabled(objectClassName.c_str()); - ImGui::PopID(); + const bool isObjectHighlighted = !filter.Filters.empty() && (filter.PassFilter(object->getName().c_str()) || filter.PassFilter(object->getClassName().c_str())); + bool objectOpen; + if (slaves.empty()) + objectOpen = drawNonExpandableObject(object,isObjectHighlighted, icon, objectColor, componentToOpen, currentSelection, clickedObject ); + else + objectOpen = drawExpandableObject(object,isObjectHighlighted, icon, objectColor, componentToOpen, currentSelection, clickedObject ); - if (doHighLight) - { - ImGui::PopStyleColor(); - } if (objectOpen && !slaves.empty()) { @@ -229,42 +251,9 @@ namespace windows { ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::PushID(slave.get()); - - const auto& slaveName = slave->getName(); - const auto slaveClassName = slave->getClassName(); - const bool isSlaveHighlighted = !filter.Filters.empty() && (filter.PassFilter(slaveName.c_str()) || filter.PassFilter(slaveClassName.c_str())); - - ImGui::TreeNodeEx(std::string(ICON_FA_CUBE " ").c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); - if (ImGui::IsItemClicked()) - { - if (ImGui::IsMouseDoubleClicked(0)) - { - componentToOpen.insert(slave.get()); - clickedObject = nullptr; - } - else - { - clickedObject = slave.get(); - } - } - ImGui::SameLine(); - - doHighLight = isSlaveHighlighted || ((clickedObject == slave.get()) != currentSelection.contains(slave.get())); - if (doHighLight) - { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); - } - ImGui::Text(slave->getName().c_str()); - ImGui::TableNextColumn(); - ImGui::TextDisabled(slave->getClassName().c_str()); - - if (doHighLight) - { - ImGui::PopStyleColor(); - } - ImGui::PopID(); + const bool isSlaveHighlighted = !filter.Filters.empty() && (filter.PassFilter(slave->getName().c_str()) || filter.PassFilter(slave->getClassName().c_str())); + drawNonExpandableObject(slave.get(), isSlaveHighlighted, ICON_FA_CUBE, ImVec4(1,1,1,1), componentToOpen, currentSelection, clickedObject ); } ImGui::TreePop(); } @@ -317,8 +306,8 @@ namespace windows openedComponents.insert(componentToOpen.begin(), componentToOpen.end()); openedComponents.insert(focusedComponents.begin(), focusedComponents.end()); - sofa::type::vector toRemove; - static std::map resizeWindow; + sofa::type::vector toRemove; + static std::map resizeWindow; for (auto* component : openedComponents) { bool isOpen = true; diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.h b/SofaImGui/src/SofaImGui/windows/SceneGraph.h index 627fbc92c5..ec4a994409 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.h +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.h @@ -40,7 +40,7 @@ namespace windows */ void showSceneGraph(sofa::core::sptr groot, const char* const& windowNameSceneGraph, - std::set& openedComponents, + std::set& openedComponents, std::set& focusedComponents, std::set& currentSelection, WindowState& winManagerSceneGraph, WindowState& winManagerSelectionDescription); @@ -62,5 +62,10 @@ namespace windows std::set& focusedComponents, WindowState& winSelectionDescription); + //Utilitaries to draw the graph + bool drawExpandableObject(sofa::core::objectmodel::Base * obj, bool isNodeHighlighted, const char* icon, const ImVec4 objectColor, std::set& componentToOpen, const std::set& currentSelection, sofa::core::objectmodel::Base* &clickedObject); + bool drawNonExpandableObject(sofa::core::objectmodel::Base * obj, bool isObjectHighlighted, const char* icon, const ImVec4 objectColor, std::set& componentToOpen, const std::set& currentSelection, sofa::core::objectmodel::Base* &clickedObject); + + } // namespace sofaimgui From 657fb1a665bac962344306b20def8415672e8a4a Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 10 Oct 2025 15:59:45 +0200 Subject: [PATCH 7/9] Fix icon coloring for expandable objects --- SofaImGui/src/SofaImGui/windows/SceneGraph.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index aa64743eef..def2732c0f 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -49,11 +49,12 @@ namespace windows { const auto& objName = obj->getName(); + ImGui::PushStyleColor(ImGuiCol_Text, objectColor); + //Tree expand drawing (tick + icon) ImGui::PushID(obj); const bool open = ImGui::TreeNode((std::string(icon) + std::string(" ")) .c_str()); ImGui::PopID(); - ImGui::PushStyleColor(ImGuiCol_Text, objectColor); //Name drawing to be able to select the node for inspection ImGui::PushID(&objName); From 8e2ac8e3e2e525b479a3b072f80a59aa572eaf2f Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 10 Oct 2025 16:27:12 +0200 Subject: [PATCH 8/9] Fix compilation --- SofaImGui/src/SofaImGui/ObjectColor.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SofaImGui/src/SofaImGui/ObjectColor.h b/SofaImGui/src/SofaImGui/ObjectColor.h index 0a5d18b38c..d81b92c0e6 100644 --- a/SofaImGui/src/SofaImGui/ObjectColor.h +++ b/SofaImGui/src/SofaImGui/ObjectColor.h @@ -22,11 +22,10 @@ #pragma once #include -#include namespace sofa::core::objectmodel { - class BaseObject; + class Base; } namespace sofaimgui From 1af2768695e8f31447d4af4c9787a8c366762516 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Wed, 15 Oct 2025 16:21:02 +0200 Subject: [PATCH 9/9] What are those changes ? --- SofaImGui/src/SofaImGui/windows/SceneGraph.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp index f3d89df045..8bb29cfa9f 100644 --- a/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp +++ b/SofaImGui/src/SofaImGui/windows/SceneGraph.cpp @@ -39,9 +39,6 @@ #include #include "SceneGraph.h" -#include -#include - namespace windows {