From 7ce4144904ef50b9fe87335a9e679e4d0bf06bb3 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Thu, 17 Jul 2025 02:42:14 +0200 Subject: [PATCH 01/14] Implement first version of a drawSelection. This is just for drafting. A lot to do in a different way --- src/sofa/qt/QSofaListView.cpp | 9 +++ src/sofa/qt/QSofaListView.h | 2 + src/sofa/qt/RealGUI.cpp | 18 ++++++ src/sofa/qt/RealGUI.h | 8 +++ src/sofa/qt/viewer/qgl/QtGLViewer.h | 2 + src/sofa/qt/viewer/qt/QtViewer.cpp | 86 +++++++++++++++++++++++++++++ src/sofa/qt/viewer/qt/QtViewer.h | 2 + 7 files changed, 127 insertions(+) diff --git a/src/sofa/qt/QSofaListView.cpp b/src/sofa/qt/QSofaListView.cpp index d4b2507..f633395 100644 --- a/src/sofa/qt/QSofaListView.cpp +++ b/src/sofa/qt/QSofaListView.cpp @@ -300,6 +300,15 @@ void QSofaListView::updateMatchingObjectmodel(QTreeWidgetItem* item, int) updateMatchingObjectmodel(item); } +Base* QSofaListView::getCurrentSelectedBase() +{ + auto items = selectedItems(); + if(items.size()==0) + return nullptr; + + return graphListener_->findObject(items[0]); +} + void QSofaListView::updateMatchingObjectmodel(QTreeWidgetItem* item) { BaseData* data = nullptr; diff --git a/src/sofa/qt/QSofaListView.h b/src/sofa/qt/QSofaListView.h index 077b0b6..9e07476 100644 --- a/src/sofa/qt/QSofaListView.h +++ b/src/sofa/qt/QSofaListView.h @@ -80,6 +80,8 @@ class SOFA_QT_API QSofaListView : public SofaSceneGraphWidget void setPropertyWidget(QDisplayPropertyWidget* propertyWid) {propertyWidget = propertyWid;} void addInPropertyWidget(QTreeWidgetItem *item, bool clear); + sofa::core::objectmodel::Base* getCurrentSelectedBase(); + void Clear(sofa::simulation::Node* rootNode); /// Updates the view so it is synchronized with the simulation graph. diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index 1f21437..f1f4838 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -499,6 +499,17 @@ void RealGUI::setTraceVisitors(bool b) //======================= METHODS ========================= { +sofa::core::objectmodel::Base* RealGUI::getCurrentSelection() const +{ + return currentSelection; +} + +void RealGUI::setCurrentSelection(sofa::core::objectmodel::Base* newSelection) +{ + currentSelection = newSelection; + getViewer()->setCurrentSelection(currentSelection); +} + void RealGUI::docBrowserVisibilityChanged(bool visibility) { if(visibility) @@ -1545,6 +1556,13 @@ void RealGUI::createSimulationGraph() connect(simulationGraph, &QSofaListView::dirtynessChanged, this, &RealGUI::sceneGraphViewDirtynessChanged); connect(simulationGraph, &QSofaListView::lockingChanged, this, &RealGUI::sceneGraphViewLockingChanged); + // Activates the hoovering visual feedback only when working in interactive mode. + if(m_enableInteraction){ + connect(simulationGraph, &QSofaListView::itemSelectionChanged, this, [this](){ + setCurrentSelection(simulationGraph->getCurrentSelectedBase()); + }); + } + connect(simulationGraph, SIGNAL( RootNodeChanged(sofa::simulation::Node*, const char*) ), this, SLOT ( newRootNode(sofa::simulation::Node* , const char*) ) ); connect(simulationGraph, SIGNAL( NodeRemoved() ), this, SLOT( update() ) ); connect(simulationGraph, SIGNAL( Lock(bool) ), this, SLOT( lockAnimation(bool) ) ); diff --git a/src/sofa/qt/RealGUI.h b/src/sofa/qt/RealGUI.h index 4d2266b..7e0cd81 100644 --- a/src/sofa/qt/RealGUI.h +++ b/src/sofa/qt/RealGUI.h @@ -385,8 +385,16 @@ protected slots: void newScene(); void newStep(); void quit(); + void currentSelectionChanged(sofa::core::objectmodel::Base* previousSelection, + sofa::core::objectmodel::Base* newSelection); //-----------------SIGNALS-SLOTS------------------------} +public: + sofa::core::objectmodel::Base* getCurrentSelection() const; + void setCurrentSelection(sofa::core::objectmodel::Base* newSelection); + +private: + sofa::core::objectmodel::Base* currentSelection {nullptr}; }; diff --git a/src/sofa/qt/viewer/qgl/QtGLViewer.h b/src/sofa/qt/viewer/qgl/QtGLViewer.h index d50566b..7274465 100644 --- a/src/sofa/qt/viewer/qgl/QtGLViewer.h +++ b/src/sofa/qt/viewer/qgl/QtGLViewer.h @@ -168,6 +168,8 @@ class SOFA_QT_API QtGLViewer :public QGLViewer, public sofa::qt::viewer::OglMo //virtual bool event ( QEvent * e ); virtual void drawScene() override; + virtual void drawSelection() override{}; + virtual void DrawLogo(void); diff --git a/src/sofa/qt/viewer/qt/QtViewer.cpp b/src/sofa/qt/viewer/qt/QtViewer.cpp index 8826ad5..379a4bf 100644 --- a/src/sofa/qt/viewer/qt/QtViewer.cpp +++ b/src/sofa/qt/viewer/qt/QtViewer.cpp @@ -569,6 +569,8 @@ void QtViewer::DisplayOBJs() sofa::simulation::node::draw(vparams, groot.get()); + + if (m_bShowAxis) { const SReal* minBBox = vparams->sceneBBox().minBBoxPtr(); @@ -945,6 +947,84 @@ void QtViewer::calcProjection(int width, int height) vparams->setProjectionMatrix(projectionMatrix); } +void QtViewer::drawSelection() +{ + + drawTool->setPolygonMode(0, false); + if(!currentSelection) + return; + + auto node = castTo(currentSelection); + if(node){ + auto box = node->f_bbox.getValue(); + drawTool->drawBoundingBox(box.minBBox(), box.maxBBox(), 2.0); + return; + } + + auto object = castTo(currentSelection); + if(object){ + auto ownerNode = dynamic_cast(object->getContext()); + if(ownerNode){ + auto box = ownerNode->f_bbox.getValue(); + drawTool->drawBoundingBox(box.minBBox(), box.maxBBox(), 2.0); + } + + auto position = object->findData("position"); + if(position){ + auto positions = dynamic_cast>*>(position); + if(positions){ + drawTool->drawPoints(positions->getValue(), 2.0, RGBAColor::yellow()); + }else{ + + auto rigidPositions = dynamic_cast>>*>(position); + if(rigidPositions){ + for(auto frame : rigidPositions->getValue()){ + float targetScreenSize = 50.0; + float screenHeight = _H; + float distance = (currentCamera->getPosition() - Rigid3Types::getCPos(frame)).norm(); + SReal scale = distance * tan(currentCamera->getFieldOfView() / 2.0f) * targetScreenSize / screenHeight; + drawTool->drawFrame(Rigid3Types::getCPos(frame), Rigid3Types::getCRot(frame), {scale, scale,scale}); + } + } + } + } + + auto triangles = object->findData("triangles"); + if(position && triangles){ + auto d_positions = dynamic_cast>*>(position); + auto d_triangles = dynamic_cast>*>(triangles); + + if(d_positions && d_triangles){ + auto positions = d_positions->getValue(); + std::vector tripoints; + for(auto indices : d_triangles->getValue()){ + if(indices[0] < positions.size() && + indices[1] < positions.size() && + indices[2] < positions.size()){ + tripoints.push_back(positions[indices[0]]); + tripoints.push_back(positions[indices[1]]); + tripoints.push_back(positions[indices[1]]); + tripoints.push_back(positions[indices[2]]); + tripoints.push_back(positions[indices[2]]); + tripoints.push_back(positions[indices[0]]); + } + } + drawTool->drawLines(tripoints, 1.5, RGBAColor::fromFloat(1.0,1.0,1.0,0.7)); + } + } + + + return; + } + + assert(false && "Only node and object can be selected, if you see this line please report to sofa-developement team"); +} + +void QtViewer::drawOverlays() +{ + +} + // --------------------------------------------------------- // --- // --------------------------------------------------------- @@ -966,6 +1046,12 @@ void QtViewer::paintGL() // draw the scene drawScene(); + // draw the visual hints on the currently selected object + + drawSelection(); + + drawOverlays(); + if(!captureTimer.isActive()) { SofaViewer::captureEvent(); diff --git a/src/sofa/qt/viewer/qt/QtViewer.h b/src/sofa/qt/viewer/qt/QtViewer.h index c110908..d1690c1 100644 --- a/src/sofa/qt/viewer/qt/QtViewer.h +++ b/src/sofa/qt/viewer/qt/QtViewer.h @@ -248,6 +248,8 @@ public slots: void DisplayOBJs(); void DisplayMenu(void); virtual void drawScene() override ; + virtual void drawSelection() override; + void drawOverlays(); void MakeStencilMask(); void ApplySceneTransformation(int x, int y); From 57238f2a4c8b6d56460913b5826a2d5d0fb0b6ac Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Thu, 24 Jul 2025 14:41:19 +0200 Subject: [PATCH 02/14] Refactor to handle sofa's PR#5636 --- src/sofa/qt/RealGUI.cpp | 2 +- src/sofa/qt/viewer/qgl/QtGLViewer.h | 2 - src/sofa/qt/viewer/qt/QtViewer.cpp | 76 +---------------------------- src/sofa/qt/viewer/qt/QtViewer.h | 1 - 4 files changed, 2 insertions(+), 79 deletions(-) diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index f1f4838..2cb4bd4 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -507,7 +507,7 @@ sofa::core::objectmodel::Base* RealGUI::getCurrentSelection() const void RealGUI::setCurrentSelection(sofa::core::objectmodel::Base* newSelection) { currentSelection = newSelection; - getViewer()->setCurrentSelection(currentSelection); + getViewer()->setCurrentSelection({currentSelection}); } void RealGUI::docBrowserVisibilityChanged(bool visibility) diff --git a/src/sofa/qt/viewer/qgl/QtGLViewer.h b/src/sofa/qt/viewer/qgl/QtGLViewer.h index 7274465..d50566b 100644 --- a/src/sofa/qt/viewer/qgl/QtGLViewer.h +++ b/src/sofa/qt/viewer/qgl/QtGLViewer.h @@ -168,8 +168,6 @@ class SOFA_QT_API QtGLViewer :public QGLViewer, public sofa::qt::viewer::OglMo //virtual bool event ( QEvent * e ); virtual void drawScene() override; - virtual void drawSelection() override{}; - virtual void DrawLogo(void); diff --git a/src/sofa/qt/viewer/qt/QtViewer.cpp b/src/sofa/qt/viewer/qt/QtViewer.cpp index 379a4bf..46c2e47 100644 --- a/src/sofa/qt/viewer/qt/QtViewer.cpp +++ b/src/sofa/qt/viewer/qt/QtViewer.cpp @@ -947,79 +947,6 @@ void QtViewer::calcProjection(int width, int height) vparams->setProjectionMatrix(projectionMatrix); } -void QtViewer::drawSelection() -{ - - drawTool->setPolygonMode(0, false); - if(!currentSelection) - return; - - auto node = castTo(currentSelection); - if(node){ - auto box = node->f_bbox.getValue(); - drawTool->drawBoundingBox(box.minBBox(), box.maxBBox(), 2.0); - return; - } - - auto object = castTo(currentSelection); - if(object){ - auto ownerNode = dynamic_cast(object->getContext()); - if(ownerNode){ - auto box = ownerNode->f_bbox.getValue(); - drawTool->drawBoundingBox(box.minBBox(), box.maxBBox(), 2.0); - } - - auto position = object->findData("position"); - if(position){ - auto positions = dynamic_cast>*>(position); - if(positions){ - drawTool->drawPoints(positions->getValue(), 2.0, RGBAColor::yellow()); - }else{ - - auto rigidPositions = dynamic_cast>>*>(position); - if(rigidPositions){ - for(auto frame : rigidPositions->getValue()){ - float targetScreenSize = 50.0; - float screenHeight = _H; - float distance = (currentCamera->getPosition() - Rigid3Types::getCPos(frame)).norm(); - SReal scale = distance * tan(currentCamera->getFieldOfView() / 2.0f) * targetScreenSize / screenHeight; - drawTool->drawFrame(Rigid3Types::getCPos(frame), Rigid3Types::getCRot(frame), {scale, scale,scale}); - } - } - } - } - - auto triangles = object->findData("triangles"); - if(position && triangles){ - auto d_positions = dynamic_cast>*>(position); - auto d_triangles = dynamic_cast>*>(triangles); - - if(d_positions && d_triangles){ - auto positions = d_positions->getValue(); - std::vector tripoints; - for(auto indices : d_triangles->getValue()){ - if(indices[0] < positions.size() && - indices[1] < positions.size() && - indices[2] < positions.size()){ - tripoints.push_back(positions[indices[0]]); - tripoints.push_back(positions[indices[1]]); - tripoints.push_back(positions[indices[1]]); - tripoints.push_back(positions[indices[2]]); - tripoints.push_back(positions[indices[2]]); - tripoints.push_back(positions[indices[0]]); - } - } - drawTool->drawLines(tripoints, 1.5, RGBAColor::fromFloat(1.0,1.0,1.0,0.7)); - } - } - - - return; - } - - assert(false && "Only node and object can be selected, if you see this line please report to sofa-developement team"); -} - void QtViewer::drawOverlays() { @@ -1047,8 +974,7 @@ void QtViewer::paintGL() drawScene(); // draw the visual hints on the currently selected object - - drawSelection(); + drawSelection(vparams); drawOverlays(); diff --git a/src/sofa/qt/viewer/qt/QtViewer.h b/src/sofa/qt/viewer/qt/QtViewer.h index d1690c1..f85e135 100644 --- a/src/sofa/qt/viewer/qt/QtViewer.h +++ b/src/sofa/qt/viewer/qt/QtViewer.h @@ -248,7 +248,6 @@ public slots: void DisplayOBJs(); void DisplayMenu(void); virtual void drawScene() override ; - virtual void drawSelection() override; void drawOverlays(); void MakeStencilMask(); From 609085a1b592cece14f27f7365847f258215b2cb Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Thu, 24 Jul 2025 18:29:50 +0200 Subject: [PATCH 03/14] Add multiple selection in the scene graph. --- src/sofa/qt/QSofaListView.cpp | 13 ++++++------- src/sofa/qt/QSofaListView.h | 2 +- src/sofa/qt/RealGUI.cpp | 13 +------------ src/sofa/qt/RealGUI.h | 12 ------------ 4 files changed, 8 insertions(+), 32 deletions(-) diff --git a/src/sofa/qt/QSofaListView.cpp b/src/sofa/qt/QSofaListView.cpp index f633395..baeb9db 100644 --- a/src/sofa/qt/QSofaListView.cpp +++ b/src/sofa/qt/QSofaListView.cpp @@ -77,7 +77,7 @@ QSofaListView::QSofaListView(const SofaListViewAttribute& attribute, setRootIsDecorated(true); setIndentation(8); - + setSelectionMode(QAbstractItemView::ExtendedSelection); graphListener_ = new GraphListenerQListView(this); this->setContextMenuPolicy(Qt::CustomContextMenu); @@ -300,13 +300,12 @@ void QSofaListView::updateMatchingObjectmodel(QTreeWidgetItem* item, int) updateMatchingObjectmodel(item); } -Base* QSofaListView::getCurrentSelectedBase() +const std::set QSofaListView::getCurrentSelectedBases() const { - auto items = selectedItems(); - if(items.size()==0) - return nullptr; - - return graphListener_->findObject(items[0]); + std::set items; + for(auto item : selectedItems()) + items.insert(graphListener_->findObject(item)); + return items; } void QSofaListView::updateMatchingObjectmodel(QTreeWidgetItem* item) diff --git a/src/sofa/qt/QSofaListView.h b/src/sofa/qt/QSofaListView.h index 9e07476..243599c 100644 --- a/src/sofa/qt/QSofaListView.h +++ b/src/sofa/qt/QSofaListView.h @@ -80,7 +80,7 @@ class SOFA_QT_API QSofaListView : public SofaSceneGraphWidget void setPropertyWidget(QDisplayPropertyWidget* propertyWid) {propertyWidget = propertyWid;} void addInPropertyWidget(QTreeWidgetItem *item, bool clear); - sofa::core::objectmodel::Base* getCurrentSelectedBase(); + const std::set getCurrentSelectedBases() const; void Clear(sofa::simulation::Node* rootNode); diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index 2cb4bd4..c1d9c00 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -499,17 +499,6 @@ void RealGUI::setTraceVisitors(bool b) //======================= METHODS ========================= { -sofa::core::objectmodel::Base* RealGUI::getCurrentSelection() const -{ - return currentSelection; -} - -void RealGUI::setCurrentSelection(sofa::core::objectmodel::Base* newSelection) -{ - currentSelection = newSelection; - getViewer()->setCurrentSelection({currentSelection}); -} - void RealGUI::docBrowserVisibilityChanged(bool visibility) { if(visibility) @@ -1559,7 +1548,7 @@ void RealGUI::createSimulationGraph() // Activates the hoovering visual feedback only when working in interactive mode. if(m_enableInteraction){ connect(simulationGraph, &QSofaListView::itemSelectionChanged, this, [this](){ - setCurrentSelection(simulationGraph->getCurrentSelectedBase()); + getViewer()->setCurrentSelection(simulationGraph->getCurrentSelectedBases()); }); } diff --git a/src/sofa/qt/RealGUI.h b/src/sofa/qt/RealGUI.h index 7e0cd81..b8db299 100644 --- a/src/sofa/qt/RealGUI.h +++ b/src/sofa/qt/RealGUI.h @@ -385,22 +385,10 @@ protected slots: void newScene(); void newStep(); void quit(); - void currentSelectionChanged(sofa::core::objectmodel::Base* previousSelection, - sofa::core::objectmodel::Base* newSelection); //-----------------SIGNALS-SLOTS------------------------} -public: - sofa::core::objectmodel::Base* getCurrentSelection() const; - void setCurrentSelection(sofa::core::objectmodel::Base* newSelection); - -private: - sofa::core::objectmodel::Base* currentSelection {nullptr}; }; - - - - struct ActivationFunctor { ActivationFunctor(bool act, GraphListenerQListView* l):active(act), listener(l) From 5076097123fc56c1fb9406594b510b0ce84a6d57 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Fri, 25 Jul 2025 10:42:47 +0200 Subject: [PATCH 04/14] Add selection parameters in the Viewer's tab and move the "documentation" in its own QDialog windget. --- CMakeLists.txt | 1 + src/sofa/qt/GUI.ui | 236 ++++++++++++++++++++++++--------- src/sofa/qt/RealGUI.cpp | 36 ++++- src/sofa/qt/RealGUI.h | 1 + src/sofa/qt/ViewerShortcuts.ui | 40 ++++++ 5 files changed, 245 insertions(+), 69 deletions(-) create mode 100644 src/sofa/qt/ViewerShortcuts.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ddef33..eddcf7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -230,6 +230,7 @@ set(UI_FILES ${SRC_ROOT}/GUI.ui ${SRC_ROOT}/MouseManager.ui ${SRC_ROOT}/PluginManager.ui + ${SRC_ROOT}/ViewerShortcuts.ui ${SRC_ROOT}/VideoRecorderManager.ui ) set(QRC_FILES diff --git a/src/sofa/qt/GUI.ui b/src/sofa/qt/GUI.ui index 4d79ff3..bc18074 100644 --- a/src/sofa/qt/GUI.ui +++ b/src/sofa/qt/GUI.ui @@ -110,6 +110,7 @@ + @@ -127,7 +128,7 @@ 200 - 551 + 594 @@ -167,38 +168,6 @@ QLayout::SetNoConstraint - - - - true - - - Set the camera to initial position and orientation - - - Reset &View - - - Alt+V - - - - - - - Launch the Simulation - - - &Animate - - - Alt+A - - - true - - - @@ -231,32 +200,6 @@ - - - - Compute the simulation at time t+DT - - - S&tep - - - Alt+T - - - true - - - - - - - Save S&creenshot - - - Alt+C - - - @@ -306,6 +249,64 @@ + + + + true + + + Set the camera to initial position and orientation + + + Reset &View + + + Alt+V + + + + + + + Save S&creenshot + + + Alt+C + + + + + + + Launch the Simulation + + + &Animate + + + Alt+A + + + true + + + + + + + Compute the simulation at time t+DT + + + S&tep + + + Alt+T + + + true + + + @@ -339,7 +340,7 @@ - 0 + 4 @@ -563,13 +564,13 @@ State 2: dirty, in that state the button reflect the fact that the scene graph v - + Viewer - + 0 @@ -644,7 +645,111 @@ State 2: dirty, in that state the button reflect the fact that the scene graph v - + + + + + + 0 + 0 + + + + Show node + + + + + + + + bounding boxes + + + + + + + + + + + + + 0 + 0 + + + + Show object + + + + + + + + bounding boxes + + + + + + + positions + + + + + + + surfaces + + + + + + + volumes + + + + + + + indices + + + + + + + + + + rendering scale + + + + + + + 0.100000000000000 + + + 0.020000000000000 + + + + + + + + + + + + @@ -823,6 +928,11 @@ State 2: dirty, in that state the button reflect the fact that the scene graph v Data Graph Window + + + Viewer's shortcuts + + diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index c1d9c00..1f04f43 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -1431,8 +1431,32 @@ void RealGUI::initViewer(BaseViewer* _viewer) sofaViewer->getQWidget(), SLOT( fitNodeBBox(sofa::core::objectmodel::BaseNode*) ) ); - // setGUI - textEdit1->setText ( sofaViewer->helpString() ); + Ui_GUI::showNodeBoundingBox->setChecked(sofaViewer->m_showSelectedNodeBoundingBox); + Ui_GUI::showObjectBoundingBox->setChecked(sofaViewer->m_showSelectedObjectBoundingBox); + Ui_GUI::showObjectPositions->setChecked(sofaViewer->m_showSelectedObjectPositions); + Ui_GUI::showObjectSurfaces->setChecked(sofaViewer->m_showSelectedObjectSurfaces); + Ui_GUI::showObjectVolumes->setChecked(sofaViewer->m_showSelectedObjectVolumes); + Ui_GUI::showObjectIndices->setChecked(sofaViewer->m_showSelectedObjectIndices); + Ui_GUI::value->setValue(sofaViewer->m_visualScaling); + + connect(showNodeBoundingBox, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedNodeBoundingBox = checked;}); + connect(showObjectBoundingBox, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedObjectBoundingBox = checked;}); + connect(showObjectPositions, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedObjectPositions = checked;}); + connect(showObjectSurfaces, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedObjectSurfaces = checked;}); + connect(showObjectVolumes, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedObjectVolumes = checked;}); + connect(showObjectIndices, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedObjectIndices = checked;}); + + connect(actionViewerShowDocumentation, &QAction::triggered, this, [this, sofaViewer](bool state){ + QDialog* dialog=new QDialog(); + auto tmp = new Ui::windowViewerShortcuts(); + tmp->setupUi(dialog); + tmp->content->setText(sofaViewer->helpString()); + dialog->open(); + }); + + connect(Ui_GUI::value, &QDoubleSpinBox::valueChanged, + this, [sofaViewer](double value){sofaViewer->m_visualScaling = value;}); + connect ( this, SIGNAL( newStep()), sofaViewer->getQWidget(), SLOT( update())); sofaViewer->getQWidget()->setFocus(); @@ -1497,7 +1521,7 @@ void RealGUI::createRecentFilesMenu() void RealGUI::createBackgroundGUIInfos() { - QWidget *colour = new QWidget(TabPage); + QWidget *colour = new QWidget(tabViewer); QHBoxLayout *colourLayout = new QHBoxLayout(colour); colourLayout->addWidget(new QLabel(QString("Colour "),colour)); @@ -1515,7 +1539,7 @@ void RealGUI::createBackgroundGUIInfos() connect( background[i], SIGNAL( returnPressed() ), this, SLOT( updateBackgroundColour() ) ); } - QWidget *image = new QWidget(TabPage); + QWidget *image = new QWidget(tabViewer); QHBoxLayout *imageLayout = new QHBoxLayout(image); imageLayout->addWidget(new QLabel(QString("Image "),image)); @@ -1528,8 +1552,8 @@ void RealGUI::createBackgroundGUIInfos() imageLayout->addWidget(backgroundImage); connect( backgroundImage, SIGNAL( returnPressed() ), this, SLOT( updateBackgroundImage() ) ); - ((QVBoxLayout*)(TabPage->layout()))->insertWidget(1,colour); - ((QVBoxLayout*)(TabPage->layout()))->insertWidget(2,image); + ((QVBoxLayout*)(tabViewer->layout()))->insertWidget(1,colour); + ((QVBoxLayout*)(tabViewer->layout()))->insertWidget(2,image); } //------------------------------------ diff --git a/src/sofa/qt/RealGUI.h b/src/sofa/qt/RealGUI.h index b8db299..a7c96f3 100644 --- a/src/sofa/qt/RealGUI.h +++ b/src/sofa/qt/RealGUI.h @@ -26,6 +26,7 @@ #include #include +#include #include "GraphListenerQListView.h" #include "QMenuFilesRecentlyOpened.h" #include "AboutSOFADialog.h" diff --git a/src/sofa/qt/ViewerShortcuts.ui b/src/sofa/qt/ViewerShortcuts.ui new file mode 100644 index 0000000..7c83048 --- /dev/null +++ b/src/sofa/qt/ViewerShortcuts.ui @@ -0,0 +1,40 @@ + + + windowViewerShortcuts + + + true + + + + 0 + 0 + 464 + 543 + + + + + 0 + 0 + + + + Viewer's shortcuts + + + false + + + false + + + + + + + + + + + From 44a9c4ae16a5cbec44af7dfc4069a39d8cbe98bc Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Sat, 26 Jul 2025 22:57:37 +0200 Subject: [PATCH 05/14] First version of the InspectorDock.ui + palette fix for color. --- CMakeLists.txt | 4 + src/sofa/qt/GUI.ui | 13 +- src/sofa/qt/QDisplayDataWidget.cpp | 3 - src/sofa/qt/QTabulationModifyObject.cpp | 1 + src/sofa/qt/RealGUI.cpp | 44 +++++- src/sofa/qt/RealGUI.h | 4 + src/sofa/qt/dockwidgets/InspectorDock.cpp | 166 ++++++++++++++++++++++ src/sofa/qt/dockwidgets/InspectorDock.h | 45 ++++++ src/sofa/qt/dockwidgets/InspectorDock.ui | 63 ++++++++ 9 files changed, 336 insertions(+), 7 deletions(-) create mode 100644 src/sofa/qt/dockwidgets/InspectorDock.cpp create mode 100644 src/sofa/qt/dockwidgets/InspectorDock.h create mode 100644 src/sofa/qt/dockwidgets/InspectorDock.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index eddcf7f..6fdd115 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ set(MOC_HEADER_FILES ${SRC_ROOT}/SofaPluginManager.h ${SRC_ROOT}/SofaSceneGraphWidget.h ${SRC_ROOT}/WDoubleLineEdit.h + ${SRC_ROOT}/dockwidgets/InspectorDock.h ) set(HEADER_FILES ${SRC_ROOT}/config.h.in @@ -222,6 +223,7 @@ set(SOURCE_FILES ${SRC_ROOT}/SofaSceneGraphWidget.cpp ${SRC_ROOT}/viewer/VisualModelPolicy.cpp ${SRC_ROOT}/QtDataRepository.cpp + ${SRC_ROOT}/dockwidgets/InspectorDock.cpp ) set(UI_FILES ${SRC_ROOT}/AboutDialog.ui @@ -232,6 +234,8 @@ set(UI_FILES ${SRC_ROOT}/PluginManager.ui ${SRC_ROOT}/ViewerShortcuts.ui ${SRC_ROOT}/VideoRecorderManager.ui + ${SRC_ROOT}/dockwidgets/InspectorDock.ui + ) set(QRC_FILES ${SRC_ROOT}/resources/RealGUI.qrc diff --git a/src/sofa/qt/GUI.ui b/src/sofa/qt/GUI.ui index bc18074..23452c2 100644 --- a/src/sofa/qt/GUI.ui +++ b/src/sofa/qt/GUI.ui @@ -128,7 +128,7 @@ 200 - 594 + 637 @@ -154,11 +154,17 @@ true - + 0 0 + + + 0 + 400 + + QLayout::SetNoConstraint @@ -280,6 +286,9 @@ Launch the Simulation + + true + &Animate diff --git a/src/sofa/qt/QDisplayDataWidget.cpp b/src/sofa/qt/QDisplayDataWidget.cpp index c3607fc..f2cb4c2 100644 --- a/src/sofa/qt/QDisplayDataWidget.cpp +++ b/src/sofa/qt/QDisplayDataWidget.cpp @@ -202,9 +202,6 @@ bool QDataSimpleEdit::createWidgets() layout->addWidget(innerWidget_.widget.lineEdit); } - - - return true; } diff --git a/src/sofa/qt/QTabulationModifyObject.cpp b/src/sofa/qt/QTabulationModifyObject.cpp index 7805b34..624d37e 100644 --- a/src/sofa/qt/QTabulationModifyObject.cpp +++ b/src/sofa/qt/QTabulationModifyObject.cpp @@ -44,6 +44,7 @@ QTabulationModifyObject::QTabulationModifyObject(QWidget* parent, vbox->setObjectName("tabVisualizationLayout"); vbox->setContentsMargins(0, 0, 0, 0); vbox->setSpacing(0); + setAutoFillBackground(true); this->setLayout(vbox); diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index 1f04f43..cc1cc2d 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -35,6 +35,8 @@ #include "SofaWindowDataGraph.h" #endif +#include +#include "dockwidgets/InspectorDock.h" #include #include @@ -149,6 +151,33 @@ class QSOFAApplication : public QApplication QCoreApplication::setOrganizationName("Sofa Consortium"); QCoreApplication::setOrganizationDomain("sofa"); QCoreApplication::setApplicationName("runSofa"); + setStyle("Fusion"); + + setStyleSheet("QToolTip { color: white; background-color: #353535; border: 1px solid white; }"); + + QPalette defaultPalette = QApplication::style()->standardPalette(); + QColor text = defaultPalette.color(QPalette::ButtonText); + + QPalette darkPalette; + darkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); + darkPalette.setColor(QPalette::WindowText, text); + darkPalette.setColor(QPalette::Base, QColor(63, 63, 63)); + darkPalette.setColor(QPalette::AlternateBase, Qt::red); + darkPalette.setColor(QPalette::ToolTipBase, QColor(35, 35, 35)); + darkPalette.setColor(QPalette::ToolTipText, text); + darkPalette.setColor(QPalette::Text, text); + darkPalette.setColor(QPalette::Button, QColor(53, 53, 53)); + darkPalette.setColor(QPalette::ButtonText, text); + darkPalette.setColor(QPalette::BrightText, Qt::red); + darkPalette.setColor(QPalette::Highlight, QColor(142, 45, 197).lighter()); + darkPalette.setColor(QPalette::HighlightedText, Qt::black); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); + darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(160, 160, 160)); + + setPalette(darkPalette); } #if QT_VERSION < 0x050000 @@ -272,6 +301,7 @@ void RealGUI::CreateApplication(int /*_argc*/, char** /*_argv*/) *argc = 1; argv[0] = strdup ( BaseGUI::GetProgramName() ); argv[1]=nullptr; + application = new QSOFAApplication ( *argc,argv ); //force locale to Standard C @@ -350,7 +380,10 @@ RealGUI::RealGUI ( const char* viewername) m_viewerMSAANbSampling(1) { setupUi(this); - + + m_inspectorDock = new InspectorDock(this); + addDockWidget(Qt::RightDockWidgetArea, m_inspectorDock); + ExpandAllButton->setIcon(QIcon(":/RealGUI/expandAll")); CollapseAllButton->setIcon(QIcon(":/RealGUI/collapseAll")); sceneGraphRefreshToggleButton->setIcon(QIcon(":/RealGUI/sceneGraphRefresh")); @@ -766,6 +799,7 @@ void RealGUI::setSceneWithoutMonitor (Node::SPtr root, const char* filename, boo simulationGraph->resizeColumnToContents(0); statWidget->CreateStats(root.get()); + m_inspectorDock->setCurrentSelection({root}); getViewer()->setScene( root, filename ); getViewer()->load(); getViewer()->resetView(); @@ -805,6 +839,9 @@ void RealGUI::unloadScene(bool _withViewer) if(_withViewer && getViewer()) getViewer()->setScene(nullptr); + + m_inspectorDock->setCurrentSelection({}); + getViewer()->setCurrentSelection({}); } //------------------------------------ @@ -1572,7 +1609,10 @@ void RealGUI::createSimulationGraph() // Activates the hoovering visual feedback only when working in interactive mode. if(m_enableInteraction){ connect(simulationGraph, &QSofaListView::itemSelectionChanged, this, [this](){ - getViewer()->setCurrentSelection(simulationGraph->getCurrentSelectedBases()); + auto selectedItems = simulationGraph->getCurrentSelectedBases(); + getViewer()->setCurrentSelection(selectedItems); + m_inspectorDock->setCurrentSelection(selectedItems); + }); } diff --git a/src/sofa/qt/RealGUI.h b/src/sofa/qt/RealGUI.h index a7c96f3..55670b0 100644 --- a/src/sofa/qt/RealGUI.h +++ b/src/sofa/qt/RealGUI.h @@ -27,10 +27,12 @@ #include #include +#include #include "GraphListenerQListView.h" #include "QMenuFilesRecentlyOpened.h" #include "AboutSOFADialog.h" #include "PickHandlerCallBacks.h" +#include "sofa/qt/dockwidgets/InspectorDock.h" #include #include @@ -172,6 +174,8 @@ class SOFA_QT_API RealGUI : public QMainWindow, public Ui::GUI, public sofa::gui std::map< helper::SofaViewerFactory::Key, QAction* > viewerMap; InformationOnPickCallBack informationOnPickCallBack; + InspectorDock* m_inspectorDock; + QWidget* currentTab; QSofaStatWidget* statWidget; QTimer* timerStep; diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp new file mode 100644 index 0000000..e05dc77 --- /dev/null +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -0,0 +1,166 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the Free * +* Software Foundation; either version 2 of the License, or (at your option) * +* any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * +* more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include "InspectorDock.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace sofa::qt +{ + +class ItemStyle +{ +public: + std::string className; + std::map map; +}; + +InspectorDock::InspectorDock(QWidget* parent) : QDockWidget(parent) +{ + setupUi(this); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); + setMinimumWidth(400); + setMaximumWidth(400); + +} + +void InspectorDock::setCurrentSelection(const std::set& bases) +{ + m_currentBases = bases; + + if(m_currentBases.empty()) + return; + + updateContentFromBase((*m_currentBases.begin()).get()); +} + +//void InspectorDock::searchCascadingStyleFor(const std::string& name) +//{ +//} + +void clearLayout(QLayout *layout) { + if (!layout) + return; + + QLayoutItem *item; + while ((item = layout->takeAt(0)) != nullptr) { + if (QWidget *widget = item->widget()) { + widget->setParent(nullptr); // Optionnel, utile si le widget est affiché ailleurs + widget->deleteLater(); // Nettoyage asynchrone (meilleur avec Qt) + } + else if (QLayout *childLayout = item->layout()) { + clearLayout(childLayout); // Appel récursif + delete childLayout; + } + delete item; + } +} + +void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) +{ + std::vector ordering={"Info", "Transform", "Properties"}; + + // Group data by their group name, if no group is given the data is in the group named "Properties" + std::map> groups; + for(auto& data : base->getDataFields()) + { + auto groupName = data->getGroup(); + if(groupName.empty()) + groupName = "Properties"; + + if(!groups.contains(groupName) ) + { + groups[groupName] = {}; + } + groups[groupName].emplace_back(data); + } + + for(auto [groupName, data] : groups) + { + if(std::find(ordering.begin(), ordering.end(), groupName)==ordering.end()) + ordering.emplace_back(groupName); + } + + clearLayout(verticalLayout); + for(auto groupName : ordering) + { + if(groups.contains(groupName)) + { + auto& datasInGroup = groups[groupName]; + + QGroupBox* box = new QGroupBox(QString::fromStdString(groupName), this); + QFormLayout* formLayout = new QFormLayout(box); + box->setLayout(formLayout); + box->setCheckable(true); + box->setMaximumWidth(350); + verticalLayout->addWidget(box); + verticalLayout->setAlignment(Qt::AlignTop); + //verticalLayout->addStretch(); + + QObject::connect(box, &QGroupBox::toggled, [box](bool checked) { + if(checked) + box->setMaximumHeight(box->layout()->totalSizeHint().height()); + else + box->setMaximumHeight(20); + }); + + for(auto data : datasInGroup) + { + ModifyObjectFlags flags; + flags.setFlagsForSofa(); + //auto dataWidget = new QDisplayDataWidget(contentWidget, data, flags); + + DataWidget::CreatorArgument dwarg; + dwarg.name = data->getName(); + dwarg.data = data; + dwarg.parent = this; + dwarg.readOnly = data->isReadOnly(); + if( dynamic_cast(data) != nullptr ) + { + // a bit of a hack for DataFileName widgets. + // A custom widget is used by default if we run this code from the Modeler + + std::string widgetName=data->getWidget(); + if( widgetName.empty() ) + { + data->setWidget("widget_filename"); + } + } + + auto dataWidget = DataWidget::CreateDataWidget(dwarg); + formLayout->addRow(QString::fromStdString(data->getName()), dataWidget); + } + } + } +} + +} // namespace sofa::qt diff --git a/src/sofa/qt/dockwidgets/InspectorDock.h b/src/sofa/qt/dockwidgets/InspectorDock.h new file mode 100644 index 0000000..ea0811b --- /dev/null +++ b/src/sofa/qt/dockwidgets/InspectorDock.h @@ -0,0 +1,45 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the Free * +* Software Foundation; either version 2 of the License, or (at your option) * +* any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * +* more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include +#include +#include +#include + +namespace sofa::qt +{ + +class SOFA_QT_API InspectorDock: public QDockWidget, public Ui::InspectorDock +{ + Q_OBJECT +public: + InspectorDock(QWidget *parent); + + void setCurrentSelection(const std::set& base); + +private: + void updateContentFromBase(sofa::core::objectmodel::Base* base); + + std::set m_currentBases; +}; + +} // namespace sofa::qt diff --git a/src/sofa/qt/dockwidgets/InspectorDock.ui b/src/sofa/qt/dockwidgets/InspectorDock.ui new file mode 100644 index 0000000..066e683 --- /dev/null +++ b/src/sofa/qt/dockwidgets/InspectorDock.ui @@ -0,0 +1,63 @@ + + + InspectorDock + + + + 0 + 0 + 344 + 594 + + + + + 0 + 0 + + + + + 200 + 594 + + + + Inspector + + + + true + + + + + + true + + + + + 0 + 0 + 324 + 555 + + + + + + + + + + + + + + + sofa/qt/config.h + + + + From a7426e83f0ed5c5499c1b40759400a7b6ab15215 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Sat, 26 Jul 2025 23:45:03 +0200 Subject: [PATCH 06/14] Improve REALGUI and InspectorDock --- src/sofa/qt/RealGUI.cpp | 29 ++++++++++++++++++++--- src/sofa/qt/dockwidgets/InspectorDock.cpp | 10 ++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index cc1cc2d..38655f3 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -153,20 +153,43 @@ class QSOFAApplication : public QApplication QCoreApplication::setApplicationName("runSofa"); setStyle("Fusion"); - setStyleSheet("QToolTip { color: white; background-color: #353535; border: 1px solid white; }"); + setStyleSheet(R"( + QTabBar::tab { + background: #2F2F2F; + color: #8A8A8A; + padding: 6px; + border: 1px solid #222; + border-bottom: none; /* pour ne pas couper le contenu */ + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + + QTabBar::tab:selected { + background: #3F3F3F; /* Couleur active */ + color: white; + } + + QTabWidget::pane { + border: 1px solid #222; + top: -1px; /* pour coller l’onglet actif au contenu */ + } + + QToolTip { color: white; background-color: #353535; border: 1px solid white; } + )"); + QPalette defaultPalette = QApplication::style()->standardPalette(); QColor text = defaultPalette.color(QPalette::ButtonText); QPalette darkPalette; - darkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); + darkPalette.setColor(QPalette::Window, QColor(63, 63, 63)); darkPalette.setColor(QPalette::WindowText, text); darkPalette.setColor(QPalette::Base, QColor(63, 63, 63)); darkPalette.setColor(QPalette::AlternateBase, Qt::red); darkPalette.setColor(QPalette::ToolTipBase, QColor(35, 35, 35)); darkPalette.setColor(QPalette::ToolTipText, text); darkPalette.setColor(QPalette::Text, text); - darkPalette.setColor(QPalette::Button, QColor(53, 53, 53)); + darkPalette.setColor(QPalette::Button, QColor(63, 63, 63)); darkPalette.setColor(QPalette::ButtonText, text); darkPalette.setColor(QPalette::BrightText, Qt::red); darkPalette.setColor(QPalette::Highlight, QColor(142, 45, 197).lighter()); diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp index e05dc77..1ef3521 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.cpp +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -49,8 +49,6 @@ InspectorDock::InspectorDock(QWidget* parent) : QDockWidget(parent) scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); setMinimumWidth(400); - setMaximumWidth(400); - } void InspectorDock::setCurrentSelection(const std::set& bases) @@ -124,7 +122,10 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) box->setMaximumWidth(350); verticalLayout->addWidget(box); verticalLayout->setAlignment(Qt::AlignTop); - //verticalLayout->addStretch(); + + formLayout->setLabelAlignment(Qt::AlignLeft |Qt::AlignVCenter); + formLayout->setAlignment(Qt::AlignLeft |Qt::AlignVCenter); + formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); QObject::connect(box, &QGroupBox::toggled, [box](bool checked) { if(checked) @@ -137,13 +138,12 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) { ModifyObjectFlags flags; flags.setFlagsForSofa(); - //auto dataWidget = new QDisplayDataWidget(contentWidget, data, flags); DataWidget::CreatorArgument dwarg; dwarg.name = data->getName(); dwarg.data = data; dwarg.parent = this; - dwarg.readOnly = data->isReadOnly(); + dwarg.readOnly = true; if( dynamic_cast(data) != nullptr ) { // a bit of a hack for DataFileName widgets. From c2bb85d1cc8df45a7b63a00cb1f890740e51a82b Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Sun, 27 Jul 2025 15:42:09 +0200 Subject: [PATCH 07/14] Up --- src/sofa/qt/GUI.ui | 28 ++++++++++++++++++++++- src/sofa/qt/RealGUI.cpp | 7 +++++- src/sofa/qt/dockwidgets/InspectorDock.cpp | 11 --------- src/sofa/qt/dockwidgets/InspectorDock.h | 1 + 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/sofa/qt/GUI.ui b/src/sofa/qt/GUI.ui index 23452c2..59e0ba1 100644 --- a/src/sofa/qt/GUI.ui +++ b/src/sofa/qt/GUI.ui @@ -103,6 +103,10 @@ + + + + @@ -349,7 +353,7 @@ - 4 + 0 @@ -942,6 +946,28 @@ State 2: dirty, in that state the button reflect the fact that the scene graph v Viewer's shortcuts + + + true + + + true + + + Inspector + + + + + true + + + true + + + Controls + + diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index 38655f3..91beca1 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -515,6 +515,12 @@ RealGUI::RealGUI ( const char* viewername) connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(showAbout())); m_filelistener = new RealGUIFileListener(this); + + connect(actionInspector, &QAction::toggled, m_inspectorDock, &QDockWidget::setVisible); + connect(m_inspectorDock, &QDockWidget::visibilityChanged, actionInspector, &QAction::setChecked); + connect(actionControls, &QAction::toggled, dockWidget, &QDockWidget::setVisible); + connect(dockWidget, &QDockWidget::visibilityChanged, actionControls, &QAction::setChecked); + } //------------------------------------ @@ -1635,7 +1641,6 @@ void RealGUI::createSimulationGraph() auto selectedItems = simulationGraph->getCurrentSelectedBases(); getViewer()->setCurrentSelection(selectedItems); m_inspectorDock->setCurrentSelection(selectedItems); - }); } diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp index 1ef3521..fda2bfa 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.cpp +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -36,13 +36,6 @@ namespace sofa::qt { -class ItemStyle -{ -public: - std::string className; - std::map map; -}; - InspectorDock::InspectorDock(QWidget* parent) : QDockWidget(parent) { setupUi(this); @@ -61,10 +54,6 @@ void InspectorDock::setCurrentSelection(const std::set #include #include +#include namespace sofa::qt { From 0b3d0efcbc7ee20c58e2597637907c8099d768c0 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Sat, 9 Aug 2025 09:14:37 +0200 Subject: [PATCH 08/14] Add custom, per-component type styling in inspector dock --- src/sofa/qt/dockwidgets/InspectorDock.cpp | 142 ++++++++++++++++++++-- src/sofa/qt/dockwidgets/InspectorDock.h | 20 +++ 2 files changed, 150 insertions(+), 12 deletions(-) diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp index fda2bfa..daf0154 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.cpp +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -33,15 +33,85 @@ #include #include +#include + namespace sofa::qt { + InspectorDock::InspectorDock(QWidget* parent) : QDockWidget(parent) { setupUi(this); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); setMinimumWidth(400); + + std::ifstream f{helper::system::DataRepository.getFile("styles/inspector/default.json")}; + itemStyle = nlohmann::json::parse(f); +} + +ItemStyle::ItemStyle(){ data = {nullptr}; } +ItemStyle::~ItemStyle(){} + +ItemStyle::ItemStyle(const ItemStyle& style) +{ + data = style.data; + if( style.readonly.has_value() ) + readonly = style.readonly; + if(style.group.has_value()) + group = style.group; + if(style.priority.has_value()) + priority = style.priority; + if(style.show.has_value()) + show = style.show; +} + +ItemStyle::ItemStyle(const ItemStyle& style, const ItemStyle& parentStyle) +{ + data = style.data; + if( style.readonly.has_value() ) + readonly = style.readonly; + else + readonly = parentStyle.readonly; + + if(style.group.has_value()) + group = style.group; + else + group = parentStyle.group; + + if(style.priority.has_value()) + priority = style.priority; + else + priority = parentStyle.priority; + + if(style.show.has_value()) + show = style.show; + else + show = parentStyle.show; +} + +std::map InspectorDock::getDataStylesFor(std::string className) +{ + std::map datastyles; + + ItemStyle defaultStyle; + defaultStyle.data = nullptr; + defaultStyle.group = "Properties"; + defaultStyle.readonly = true; + defaultStyle.show = true; + defaultStyle.priority = 1; + + datastyles["name"] = defaultStyle; + + return datastyles; +} + +ItemStyle InspectorDock::getDataStyle(const std::string& dataName, + const std::map& allDataStyles, const ItemStyle& parentStyle) +{ + if(allDataStyles.contains(dataName)) + return ItemStyle(allDataStyles.at(dataName), parentStyle); + return ItemStyle{parentStyle}; } void InspectorDock::setCurrentSelection(const std::set& bases) @@ -74,21 +144,49 @@ void clearLayout(QLayout *layout) { void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) { - std::vector ordering={"Info", "Transform", "Properties"}; + std::vector ordering={"Info", "Properties", "States", "Forces", "Visualization", "Transformation"}; + + // search for style matching the class Name + std::map styles = getDataStylesFor(base->getClassName()); + + ItemStyle defaultStyle; + defaultStyle.data = nullptr; + defaultStyle.group = "Properties"; + defaultStyle.readonly = true; + defaultStyle.show = true; + defaultStyle.priority = 1; + + // Compute the style for each data + std::map dataStyles; + for(auto& data : base->getDataFields()) + { + // Get per class style + ItemStyle classStyle = getDataStyle(data->getName(), styles, defaultStyle); + + // Get per instance style + ItemStyle instanceStyle {classStyle}; + instanceStyle.data = data ; + auto groupName = data->getGroup(); + if(!groupName.empty()) + instanceStyle.group = data->getGroup(); + + dataStyles[data] = instanceStyle; + } // Group data by their group name, if no group is given the data is in the group named "Properties" std::map> groups; for(auto& data : base->getDataFields()) { - auto groupName = data->getGroup(); - if(groupName.empty()) - groupName = "Properties"; + auto style = dataStyles[data]; + auto groupName = style.group.value(); if(!groups.contains(groupName) ) { groups[groupName] = {}; } - groups[groupName].emplace_back(data); + + if(style.show.value()) + groups[groupName].emplace_back(data); } for(auto [groupName, data] : groups) @@ -117,22 +215,42 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); QObject::connect(box, &QGroupBox::toggled, [box](bool checked) { - if(checked) - box->setMaximumHeight(box->layout()->totalSizeHint().height()); - else - box->setMaximumHeight(20); + if(checked){ + for (int i = 0; i < box->layout()->count(); ++i) { + if( (i/2) % 2 ) + { + QLayoutItem* label = box->layout()->itemAt(i); + QLayoutItem* content = box->layout()->itemAt(i+1); + label->widget()->show(); // cache le widget + content->widget()->show(); // cache le widget + } + } + }else{ + //box->setMaximumHeight(20); + for (int i = 0; i < box->layout()->count(); ++i) { + if( (i/2) % 2 ) + { + QLayoutItem* label = box->layout()->itemAt(i); + QLayoutItem* content = box->layout()->itemAt(i+1); + label->widget()->hide(); // cache le widget + //content->widget()->hide(); // cache le widget + } + } + } }); for(auto data : datasInGroup) { + auto& dataStyle = dataStyles[data]; + ModifyObjectFlags flags; flags.setFlagsForSofa(); DataWidget::CreatorArgument dwarg; - dwarg.name = data->getName(); - dwarg.data = data; dwarg.parent = this; - dwarg.readOnly = true; + dwarg.data = dataStyle.data; + dwarg.name = dataStyle.data->getName(); + dwarg.readOnly = dataStyle.readonly.value(); if( dynamic_cast(data) != nullptr ) { // a bit of a hack for DataFileName widgets. diff --git a/src/sofa/qt/dockwidgets/InspectorDock.h b/src/sofa/qt/dockwidgets/InspectorDock.h index 16fef90..0ad810d 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.h +++ b/src/sofa/qt/dockwidgets/InspectorDock.h @@ -29,6 +29,21 @@ namespace sofa::qt { +class ItemStyle +{ +public: + std::optional group; + std::optional priority; + std::optional readonly; + std::optional show; + sofa::core::objectmodel::BaseData* data; + + ItemStyle(); + virtual ~ItemStyle(); + ItemStyle(const ItemStyle& style); + ItemStyle(const ItemStyle& style, const ItemStyle& parentStyle); +}; + class SOFA_QT_API InspectorDock: public QDockWidget, public Ui::InspectorDock { Q_OBJECT @@ -41,6 +56,11 @@ class SOFA_QT_API InspectorDock: public QDockWidget, public Ui::InspectorDock void updateContentFromBase(sofa::core::objectmodel::Base* base); std::set m_currentBases; + nlohmann::json itemStyle; + + std::map getDataStylesFor(std::string className); + ItemStyle getDataStyle(const std::string &dataName, + const std::map& allDataStyles, const ItemStyle& parentStyle); }; } // namespace sofa::qt From 8ed9344874fa5aaa611286e00534a0982c585e18 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 18 Aug 2025 12:56:35 +0200 Subject: [PATCH 09/14] FIX the visibility control of dock from the menubar --- src/sofa/qt/RealGUI.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index 91beca1..0abee22 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -516,11 +516,18 @@ RealGUI::RealGUI ( const char* viewername) m_filelistener = new RealGUIFileListener(this); - connect(actionInspector, &QAction::toggled, m_inspectorDock, &QDockWidget::setVisible); - connect(m_inspectorDock, &QDockWidget::visibilityChanged, actionInspector, &QAction::setChecked); - connect(actionControls, &QAction::toggled, dockWidget, &QDockWidget::setVisible); - connect(dockWidget, &QDockWidget::visibilityChanged, actionControls, &QAction::setChecked); - + // Replace the menu's actions by the one generated from the docks. + auto action = m_inspectorDock->toggleViewAction(); + action->setText(actionInspector->text()); + View->insertAction(actionInspector, action); + View->removeAction(actionInspector); + actionInspector = action; + + action = dockWidget->toggleViewAction(); + action->setText(actionControls->text()); + View->insertAction(actionControls, action); + View->removeAction(actionControls); + actionControls = action; } //------------------------------------ From 5d33a5a0beeedaaf0f37811f5db7878c5690f0d8 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 18 Aug 2025 13:25:05 +0200 Subject: [PATCH 10/14] Fix width rescaling in inspector. --- src/sofa/qt/dockwidgets/InspectorDock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp index daf0154..f11d4df 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.cpp +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -206,7 +206,7 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) QFormLayout* formLayout = new QFormLayout(box); box->setLayout(formLayout); box->setCheckable(true); - box->setMaximumWidth(350); + //box->setMaximumWidth(350); verticalLayout->addWidget(box); verticalLayout->setAlignment(Qt::AlignTop); From 87acf58238ffd0b8d131b4f90b6a40f036f9405e Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 18 Aug 2025 15:58:59 +0200 Subject: [PATCH 11/14] Remove styling experiments. --- src/sofa/qt/dockwidgets/InspectorDock.cpp | 183 +++++++--------------- src/sofa/qt/dockwidgets/InspectorDock.h | 21 --- 2 files changed, 58 insertions(+), 146 deletions(-) diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp index f11d4df..60bfea0 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.cpp +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -33,8 +33,6 @@ #include #include -#include - namespace sofa::qt { @@ -44,74 +42,7 @@ InspectorDock::InspectorDock(QWidget* parent) : QDockWidget(parent) setupUi(this); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); - setMinimumWidth(400); - - std::ifstream f{helper::system::DataRepository.getFile("styles/inspector/default.json")}; - itemStyle = nlohmann::json::parse(f); -} - -ItemStyle::ItemStyle(){ data = {nullptr}; } -ItemStyle::~ItemStyle(){} - -ItemStyle::ItemStyle(const ItemStyle& style) -{ - data = style.data; - if( style.readonly.has_value() ) - readonly = style.readonly; - if(style.group.has_value()) - group = style.group; - if(style.priority.has_value()) - priority = style.priority; - if(style.show.has_value()) - show = style.show; -} - -ItemStyle::ItemStyle(const ItemStyle& style, const ItemStyle& parentStyle) -{ - data = style.data; - if( style.readonly.has_value() ) - readonly = style.readonly; - else - readonly = parentStyle.readonly; - - if(style.group.has_value()) - group = style.group; - else - group = parentStyle.group; - - if(style.priority.has_value()) - priority = style.priority; - else - priority = parentStyle.priority; - - if(style.show.has_value()) - show = style.show; - else - show = parentStyle.show; -} - -std::map InspectorDock::getDataStylesFor(std::string className) -{ - std::map datastyles; - - ItemStyle defaultStyle; - defaultStyle.data = nullptr; - defaultStyle.group = "Properties"; - defaultStyle.readonly = true; - defaultStyle.show = true; - defaultStyle.priority = 1; - - datastyles["name"] = defaultStyle; - - return datastyles; -} - -ItemStyle InspectorDock::getDataStyle(const std::string& dataName, - const std::map& allDataStyles, const ItemStyle& parentStyle) -{ - if(allDataStyles.contains(dataName)) - return ItemStyle(allDataStyles.at(dataName), parentStyle); - return ItemStyle{parentStyle}; + setMinimumWidth(350); } void InspectorDock::setCurrentSelection(const std::set& bases) @@ -131,11 +62,11 @@ void clearLayout(QLayout *layout) { QLayoutItem *item; while ((item = layout->takeAt(0)) != nullptr) { if (QWidget *widget = item->widget()) { - widget->setParent(nullptr); // Optionnel, utile si le widget est affiché ailleurs - widget->deleteLater(); // Nettoyage asynchrone (meilleur avec Qt) + widget->setParent(nullptr); // To be sure the widget is not attached somewhere else. + widget->deleteLater(); // Asynchronous delete } else if (QLayout *childLayout = item->layout()) { - clearLayout(childLayout); // Appel récursif + clearLayout(childLayout); delete childLayout; } delete item; @@ -146,47 +77,23 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) { std::vector ordering={"Info", "Properties", "States", "Forces", "Visualization", "Transformation"}; - // search for style matching the class Name - std::map styles = getDataStylesFor(base->getClassName()); - - ItemStyle defaultStyle; - defaultStyle.data = nullptr; - defaultStyle.group = "Properties"; - defaultStyle.readonly = true; - defaultStyle.show = true; - defaultStyle.priority = 1; - - // Compute the style for each data - std::map dataStyles; - for(auto& data : base->getDataFields()) - { - // Get per class style - ItemStyle classStyle = getDataStyle(data->getName(), styles, defaultStyle); - - // Get per instance style - ItemStyle instanceStyle {classStyle}; - instanceStyle.data = data ; - auto groupName = data->getGroup(); - if(!groupName.empty()) - instanceStyle.group = data->getGroup(); - dataStyles[data] = instanceStyle; - } + setWindowTitle(QString::fromStdString("Inspector ("+base->getName()+")")); // Group data by their group name, if no group is given the data is in the group named "Properties" std::map> groups; for(auto& data : base->getDataFields()) { - auto style = dataStyles[data]; - auto groupName = style.group.value(); + auto groupName = data->getGroup(); + if(groupName.empty()) + groupName = "Properties"; if(!groups.contains(groupName) ) { groups[groupName] = {}; } - if(style.show.value()) - groups[groupName].emplace_back(data); + groups[groupName].emplace_back(data); } for(auto [groupName, data] : groups) @@ -196,6 +103,38 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) } clearLayout(verticalLayout); + + // Create the Info group as it contains elements that are not data. + QGroupBox* box = new QGroupBox(QString::fromStdString("Type"), this); + QFormLayout* formLayout = new QFormLayout(box); + box->setLayout(formLayout); + box->setCheckable(false); + verticalLayout->addWidget(box); + verticalLayout->setAlignment(Qt::AlignTop); + + formLayout->setLabelAlignment(Qt::AlignLeft |Qt::AlignVCenter); + formLayout->setAlignment(Qt::AlignLeft |Qt::AlignVCenter); + formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + + // Add a lambda to hide/check the elements depending wether the group is check/hidden + QObject::connect(box, &QGroupBox::toggled, [box](bool groupChecked) { + if(groupChecked){ + for (int i = 0; i < box->layout()->count(); ++i) { + QLayoutItem* label = box->layout()->itemAt(i); + label->widget()->show(); + } + }else{ + for (int i = 0; i < box->layout()->count(); ++i) { + QLayoutItem* label = box->layout()->itemAt(i); + label->widget()->hide(); + } + } + }); + formLayout->addRow("Class name:", new QLabel(QString::fromStdString(base->getClassName()))); + if(!base->getTemplateName().empty()) + formLayout->addRow("Parametric type:", new QLabel(QString::fromStdString(base->getTemplateName()))); + + // Creates all the other groups... for(auto groupName : ordering) { if(groups.contains(groupName)) @@ -206,7 +145,6 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) QFormLayout* formLayout = new QFormLayout(box); box->setLayout(formLayout); box->setCheckable(true); - //box->setMaximumWidth(350); verticalLayout->addWidget(box); verticalLayout->setAlignment(Qt::AlignTop); @@ -214,43 +152,31 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) formLayout->setAlignment(Qt::AlignLeft |Qt::AlignVCenter); formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); - QObject::connect(box, &QGroupBox::toggled, [box](bool checked) { - if(checked){ + // Add a lambda to hide/check the elements depending wether the group is check/hidden + QObject::connect(box, &QGroupBox::toggled, [box](bool groupChecked) { + if(groupChecked){ for (int i = 0; i < box->layout()->count(); ++i) { - if( (i/2) % 2 ) - { - QLayoutItem* label = box->layout()->itemAt(i); - QLayoutItem* content = box->layout()->itemAt(i+1); - label->widget()->show(); // cache le widget - content->widget()->show(); // cache le widget - } + QLayoutItem* label = box->layout()->itemAt(i); + label->widget()->show(); } }else{ - //box->setMaximumHeight(20); for (int i = 0; i < box->layout()->count(); ++i) { - if( (i/2) % 2 ) - { - QLayoutItem* label = box->layout()->itemAt(i); - QLayoutItem* content = box->layout()->itemAt(i+1); - label->widget()->hide(); // cache le widget - //content->widget()->hide(); // cache le widget - } + QLayoutItem* label = box->layout()->itemAt(i); + label->widget()->hide(); } } }); for(auto data : datasInGroup) { - auto& dataStyle = dataStyles[data]; - ModifyObjectFlags flags; flags.setFlagsForSofa(); DataWidget::CreatorArgument dwarg; dwarg.parent = this; - dwarg.data = dataStyle.data; - dwarg.name = dataStyle.data->getName(); - dwarg.readOnly = dataStyle.readonly.value(); + dwarg.data = data; + dwarg.name = data->getName(); + dwarg.readOnly = data->isReadOnly(); if( dynamic_cast(data) != nullptr ) { // a bit of a hack for DataFileName widgets. @@ -264,6 +190,13 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) } auto dataWidget = DataWidget::CreateDataWidget(dwarg); + if(!dwarg.readOnly) + { + connect(dataWidget, &DataWidget::WidgetDirty, this, [dataWidget](bool){ + dataWidget->updateDataValue(); + }); + } + formLayout->addRow(QString::fromStdString(data->getName()), dataWidget); } } diff --git a/src/sofa/qt/dockwidgets/InspectorDock.h b/src/sofa/qt/dockwidgets/InspectorDock.h index 0ad810d..ea0811b 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.h +++ b/src/sofa/qt/dockwidgets/InspectorDock.h @@ -24,26 +24,10 @@ #include #include #include -#include namespace sofa::qt { -class ItemStyle -{ -public: - std::optional group; - std::optional priority; - std::optional readonly; - std::optional show; - sofa::core::objectmodel::BaseData* data; - - ItemStyle(); - virtual ~ItemStyle(); - ItemStyle(const ItemStyle& style); - ItemStyle(const ItemStyle& style, const ItemStyle& parentStyle); -}; - class SOFA_QT_API InspectorDock: public QDockWidget, public Ui::InspectorDock { Q_OBJECT @@ -56,11 +40,6 @@ class SOFA_QT_API InspectorDock: public QDockWidget, public Ui::InspectorDock void updateContentFromBase(sofa::core::objectmodel::Base* base); std::set m_currentBases; - nlohmann::json itemStyle; - - std::map getDataStylesFor(std::string className); - ItemStyle getDataStyle(const std::string &dataName, - const std::map& allDataStyles, const ItemStyle& parentStyle); }; } // namespace sofa::qt From bb4c078917efcfba7e4830b0b517531e20b99e43 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 18 Aug 2025 15:59:15 +0200 Subject: [PATCH 12/14] Fix the viewer's documentation. --- src/sofa/qt/RealGUI.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index 0abee22..b24d8e0 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -528,6 +528,18 @@ RealGUI::RealGUI ( const char* viewername) View->insertAction(actionControls, action); View->removeAction(actionControls); actionControls = action; + + connect(actionViewerShowDocumentation, &QAction::triggered, this, [this](bool){ + QDialog* dialog=new QDialog(); + auto tmp = new Ui::windowViewerShortcuts(); + tmp->setupUi(dialog); + sofa::qt::viewer::SofaViewer* sofaViewer = dynamic_cast(getViewer()); + if(sofaViewer) + tmp->content->setText(sofaViewer->helpString()); + else + tmp->content->setText("No documentation for this viewer"); + dialog->open(); + }); } //------------------------------------ @@ -1519,14 +1531,6 @@ void RealGUI::initViewer(BaseViewer* _viewer) connect(showObjectVolumes, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedObjectVolumes = checked;}); connect(showObjectIndices, &QCheckBox::clicked, this, [this, sofaViewer](bool checked){sofaViewer->m_showSelectedObjectIndices = checked;}); - connect(actionViewerShowDocumentation, &QAction::triggered, this, [this, sofaViewer](bool state){ - QDialog* dialog=new QDialog(); - auto tmp = new Ui::windowViewerShortcuts(); - tmp->setupUi(dialog); - tmp->content->setText(sofaViewer->helpString()); - dialog->open(); - }); - connect(Ui_GUI::value, &QDoubleSpinBox::valueChanged, this, [sofaViewer](double value){sofaViewer->m_visualScaling = value;}); From f92bddb3293374fa21ef1ed170fb7dfe346d808a Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 18 Aug 2025 16:20:27 +0200 Subject: [PATCH 13/14] Add tooltip to the data in the inspector. --- src/sofa/qt/RealGUI.cpp | 2 +- src/sofa/qt/dockwidgets/InspectorDock.cpp | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/sofa/qt/RealGUI.cpp b/src/sofa/qt/RealGUI.cpp index b24d8e0..4e0b56c 100644 --- a/src/sofa/qt/RealGUI.cpp +++ b/src/sofa/qt/RealGUI.cpp @@ -537,7 +537,7 @@ RealGUI::RealGUI ( const char* viewername) if(sofaViewer) tmp->content->setText(sofaViewer->helpString()); else - tmp->content->setText("No documentation for this viewer"); + tmp->content->setText("There is no documentation for this viewer"); dialog->open(); }); } diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp index 60bfea0..7f99604 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.cpp +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -196,8 +196,27 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) dataWidget->updateDataValue(); }); } - - formLayout->addRow(QString::fromStdString(data->getName()), dataWidget); + auto label = new QLabel(QString::fromStdString((data->getName()))); + formLayout->addRow(label, dataWidget); + + // Build the tooltip associated with this data + const std::string& help = data->getHelp().c_str(); + const std::string valuetype = data->getValueTypeString(); + const std::string defaultValue = data->getDefaultValueString(); + std::stringstream s; + + s << "" << (!help.empty() ? help : "< No help found >") + << "\n" + << "\nData type: " << valuetype; + if (!defaultValue.empty()) + { + s << "\nDefault value: " << defaultValue; + } + label->setToolTip(QString::fromStdString(s.str())); + if(dataWidget) + dataWidget->setToolTip(QString::fromStdString(s.str())); + else + dmsg_error("InspectorDock") << "Missing data widget"; } } } From 4307b38f07bdb72c0f22c56dbfad0b6028ae0d74 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 18 Aug 2025 16:41:53 +0200 Subject: [PATCH 14/14] Add default widget when unable to create one. --- src/sofa/qt/dockwidgets/InspectorDock.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sofa/qt/dockwidgets/InspectorDock.cpp b/src/sofa/qt/dockwidgets/InspectorDock.cpp index 7f99604..a698e2a 100644 --- a/src/sofa/qt/dockwidgets/InspectorDock.cpp +++ b/src/sofa/qt/dockwidgets/InspectorDock.cpp @@ -190,6 +190,14 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) } auto dataWidget = DataWidget::CreateDataWidget(dwarg); + if (dataWidget == nullptr) + { + dataWidget = new QDataSimpleEdit(this,dwarg.data->getName().c_str(), dwarg.data); + dataWidget->createWidgets(); + dataWidget->setDataReadOnly(dwarg.readOnly); + dwarg.readOnly=true; + } + if(!dwarg.readOnly) { connect(dataWidget, &DataWidget::WidgetDirty, this, [dataWidget](bool){ @@ -213,10 +221,7 @@ void InspectorDock::updateContentFromBase(sofa::core::objectmodel::Base* base) s << "\nDefault value: " << defaultValue; } label->setToolTip(QString::fromStdString(s.str())); - if(dataWidget) - dataWidget->setToolTip(QString::fromStdString(s.str())); - else - dmsg_error("InspectorDock") << "Missing data widget"; + dataWidget->setToolTip(QString::fromStdString(s.str())); } } }