From f13342ff300aa2c25db24e786d6e44bb34ab39cd Mon Sep 17 00:00:00 2001 From: AndreaRigoni Date: Mon, 23 Mar 2026 17:46:42 +0000 Subject: [PATCH] vtkProperties --- app/gcompose/CMakeLists.txt | 4 + app/gcompose/src/ContextPanel.cpp | 59 +----- app/gcompose/src/ContextPanel.h | 35 ++-- app/gcompose/src/MainPanel.cpp | 12 +- app/gcompose/src/MainPanel.h | 1 + app/gcompose/src/PropertiesPanel.cpp | 45 +++++ app/gcompose/src/PropertiesPanel.h | 35 ++++ app/gcompose/src/PropertyWidgets.cpp | 173 ++++++++++++++++++ app/gcompose/src/PropertyWidgets.h | 110 +++++++++++ app/gcompose/src/QViewportPane.cpp | 68 ++++++- app/gcompose/src/QViewportPane.h | 16 ++ app/gcompose/src/StyleManager.cpp | 64 ++++++- app/gcompose/src/ViewportPane.cpp | 125 +++++++------ app/gcompose/src/ViewportPane.h | 16 ++ src/Core/Object.h | 9 + src/Core/Property.h | 11 +- .../vtkVoxRaytracerRepresentation.h | 2 +- src/Vtk/uLibVtkInterface.cxx | 23 ++- src/Vtk/uLibVtkInterface.h | 123 +++++++++---- 19 files changed, 745 insertions(+), 186 deletions(-) create mode 100644 app/gcompose/src/PropertiesPanel.cpp create mode 100644 app/gcompose/src/PropertiesPanel.h create mode 100644 app/gcompose/src/PropertyWidgets.cpp create mode 100644 app/gcompose/src/PropertyWidgets.h diff --git a/app/gcompose/CMakeLists.txt b/app/gcompose/CMakeLists.txt index 961fe98..7949b78 100644 --- a/app/gcompose/CMakeLists.txt +++ b/app/gcompose/CMakeLists.txt @@ -13,6 +13,10 @@ add_executable(gcompose src/ContextModel.cpp src/StyleManager.h src/StyleManager.cpp + src/PropertyWidgets.h + src/PropertyWidgets.cpp + src/PropertiesPanel.h + src/PropertiesPanel.cpp ) set_target_properties(gcompose PROPERTIES diff --git a/app/gcompose/src/ContextPanel.cpp b/app/gcompose/src/ContextPanel.cpp index 0a72630..3496d64 100644 --- a/app/gcompose/src/ContextPanel.cpp +++ b/app/gcompose/src/ContextPanel.cpp @@ -1,5 +1,7 @@ #include "ContextPanel.h" #include "ContextModel.h" +#include "PropertyWidgets.h" +#include "PropertiesPanel.h" #include #include #include @@ -8,12 +10,10 @@ #include #include #include -#include -#include ContextPanel::ContextPanel(QWidget* parent) : QWidget(parent) - , m_vtkContext(nullptr) { + , m_context(nullptr) { m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(0); @@ -43,11 +43,11 @@ ContextPanel::ContextPanel(QWidget* parent) m_splitter = new QSplitter(Qt::Vertical, this); m_splitter->addWidget(m_treeView); - m_vtkView = new uLib::Vtk::QViewport(m_splitter); - m_splitter->addWidget(m_vtkView); + m_propertiesPanel = new PropertiesPanel(m_splitter); + m_splitter->addWidget(m_propertiesPanel); QList sizes; - sizes << 400 << 200; + sizes << 400 << 600; m_splitter->setSizes(sizes); m_layout->addWidget(m_splitter); @@ -60,7 +60,6 @@ ContextPanel::ContextPanel(QWidget* parent) auto selectedIndexes = m_treeView->selectionModel()->selectedIndexes(); if (selectedIndexes.isEmpty() || !m_context) return; - // Collect objects to remove to avoid iterator invalidation issues if context signal emits during removal std::vector toRemove; for (const auto& idx : selectedIndexes) { if (idx.column() == 0) { @@ -80,47 +79,6 @@ void ContextPanel::setContext(uLib::ObjectsContext* context) { m_context = context; m_model->setContext(context); m_treeView->expandAll(); - - if (m_vtkContext) { - m_vtkView->RemovePuppet(*m_vtkContext); - delete m_vtkContext; - } - m_vtkContext = new uLib::Vtk::vtkObjectsContext(context); - // m_vtkView->AddPuppet(*m_vtkContext); // redundant: child puppets are added individually - - // Render viewport and add child puppets when context is updated - if (context) { - uLib::Object::connect(m_vtkContext, &uLib::Vtk::vtkObjectsContext::PuppetAdded, [this](uLib::Vtk::Puppet* p) { - if (this->m_vtkView && p) { - this->m_vtkView->AddPuppet(*p); - this->m_vtkView->ZoomAuto(); - this->m_vtkView->Render(); - } - }); - - uLib::Object::connect(m_vtkContext, &uLib::Vtk::vtkObjectsContext::PuppetRemoved, [this](uLib::Vtk::Puppet* p) { - if (this->m_vtkView && p) { - this->m_vtkView->RemovePuppet(*p); - this->m_vtkView->Render(); - } - }); - - // Add any puppets that were created during m_vtkContext's construction - for (auto* obj : context->GetObjects()) { - if (auto* p = m_vtkContext->GetPuppet(obj)) { - this->m_vtkView->AddPuppet(*p); - } - } - - uLib::Object::connect(context, &uLib::Object::Updated, [this]() { - if (this->m_vtkView) { - this->m_vtkView->ZoomAuto(); - this->m_vtkView->Render(); - } - }); - } - - m_vtkView->Render(); } void ContextPanel::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { @@ -131,8 +89,5 @@ void ContextPanel::onSelectionChanged(const QItemSelection& selected, const QIte emit objectSelected(target); - if (m_vtkContext) { - auto* puppet = m_vtkContext->GetPuppet(target); - m_vtkView->SelectPuppet(puppet); - } + m_propertiesPanel->setObject(target); } diff --git a/app/gcompose/src/ContextPanel.h b/app/gcompose/src/ContextPanel.h index 2447465..dcb8cdd 100644 --- a/app/gcompose/src/ContextPanel.h +++ b/app/gcompose/src/ContextPanel.h @@ -1,50 +1,43 @@ -#ifndef CONTEXT_PANEL_H -#define CONTEXT_PANEL_H +#ifndef CONTEXTPANEL_H +#define CONTEXTPANEL_H #include #include +#include "Core/Object.h" -class QTreeView; class QVBoxLayout; +class QHBoxLayout; class QLabel; -class ContextModel; +class QTreeView; class QSplitter; - - -namespace uLib { - class Object; - class ObjectsContext; - namespace Vtk { - class QViewport; - class vtkObjectsContext; - } -} +class ContextModel; +namespace uLib { class ObjectsContext; } class ContextPanel : public QWidget { Q_OBJECT public: - explicit ContextPanel(QWidget* parent = nullptr); - virtual ~ContextPanel(); + ContextPanel(QWidget* parent = nullptr); + ~ContextPanel(); void setContext(uLib::ObjectsContext* context); -Q_SIGNALS: +signals: void objectSelected(uLib::Object* obj); -private Q_SLOTS: +private slots: void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); private: QVBoxLayout* m_layout; QWidget* m_titleBar; QLabel* m_titleLabel; + QTreeView* m_treeView; ContextModel* m_model; QSplitter* m_splitter; - uLib::Vtk::QViewport* m_vtkView; - uLib::Vtk::vtkObjectsContext* m_vtkContext; + class PropertiesPanel* m_propertiesPanel; uLib::ObjectsContext* m_context; }; -#endif // CONTEXT_PANEL_H +#endif // CONTEXTPANEL_H diff --git a/app/gcompose/src/MainPanel.cpp b/app/gcompose/src/MainPanel.cpp index 17f7d58..ba09995 100644 --- a/app/gcompose/src/MainPanel.cpp +++ b/app/gcompose/src/MainPanel.cpp @@ -1,6 +1,7 @@ #include "MainPanel.h" #include "ViewportPane.h" #include "ContextPanel.h" +#include "PropertiesPanel.h" #include "Core/ObjectFactory.h" #include "Core/ObjectsContext.h" #include "Vtk/vtkObjectsContext.h" @@ -74,8 +75,11 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_m m_rootSplitter = new QSplitter(Qt::Horizontal, this); m_contextPanel = new ContextPanel(m_rootSplitter); m_rootSplitter->addWidget(m_contextPanel); + m_rootSplitter->setStretchFactor(0, 0); + m_firstPane = new ViewportPane(m_rootSplitter); m_rootSplitter->addWidget(m_firstPane); + m_rootSplitter->setStretchFactor(1, 1); connect(m_contextPanel, &ContextPanel::objectSelected, [this](uLib::Object* obj) { if (auto* viewport = qobject_cast(m_firstPane->currentViewport())) { @@ -84,12 +88,16 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_m puppet = m_mainVtkContext->GetPuppet(obj); } viewport->SelectPuppet(puppet); + // Update the display properties in the viewport pane itself - use the puppet proxy if possible + m_firstPane->setObject(puppet ? (uLib::Object*)puppet : obj); + } else { + m_firstPane->setObject(obj); } }); - // Set initial sizes + // Set initial sizes: Context(250), Viewport(600), Properties(250) QList sizes; - sizes << 200 << 1000; + sizes << 250 << 600 << 250; m_rootSplitter->setSizes(sizes); mainLayout->addWidget(m_rootSplitter, 1); diff --git a/app/gcompose/src/MainPanel.h b/app/gcompose/src/MainPanel.h index 72086ca..6ea39f7 100644 --- a/app/gcompose/src/MainPanel.h +++ b/app/gcompose/src/MainPanel.h @@ -6,6 +6,7 @@ class QSplitter; class ViewportPane; class ContextPanel; +class PropertiesPanel; namespace uLib { class ObjectsContext; diff --git a/app/gcompose/src/PropertiesPanel.cpp b/app/gcompose/src/PropertiesPanel.cpp new file mode 100644 index 0000000..52a8a1e --- /dev/null +++ b/app/gcompose/src/PropertiesPanel.cpp @@ -0,0 +1,45 @@ +#include "PropertiesPanel.h" +#include "PropertyWidgets.h" +#include +#include +#include +#include "Core/Object.h" + +PropertiesPanel::PropertiesPanel(QWidget* parent) : QWidget(parent) { + this->setObjectName("PropertiesPanel"); + m_layout = new QVBoxLayout(this); + m_layout->setContentsMargins(0, 0, 0, 0); + m_layout->setSpacing(0); + + // Title bar + m_titleBar = new QWidget(this); + m_titleBar->setObjectName("PaneTitleBar"); + m_titleBar->setFixedHeight(22); + + auto* titleLayout = new QHBoxLayout(m_titleBar); + titleLayout->setContentsMargins(5, 0, 5, 0); + + m_titleLabel = new QLabel("Properties", m_titleBar); + m_titleLabel->setObjectName("TitleLabel"); + titleLayout->addWidget(m_titleLabel); + titleLayout->addStretch(); + + m_layout->addWidget(m_titleBar); + + // Editor + m_editor = new uLib::Qt::PropertyEditor(this); + m_layout->addWidget(m_editor, 1); +} + +void PropertiesPanel::setObject(uLib::Object* obj) { + if (obj) { + m_titleLabel->setText(QString("Properties: %1 (%2)") + .arg(QString::fromStdString(obj->GetInstanceName())) + .arg(obj->GetClassName())); + } else { + m_titleLabel->setText("Properties: (No selection)"); + } + m_editor->setObject(obj); +} + +PropertiesPanel::~PropertiesPanel() {} diff --git a/app/gcompose/src/PropertiesPanel.h b/app/gcompose/src/PropertiesPanel.h new file mode 100644 index 0000000..cff62aa --- /dev/null +++ b/app/gcompose/src/PropertiesPanel.h @@ -0,0 +1,35 @@ +#ifndef PROPERTIES_PANEL_H +#define PROPERTIES_PANEL_H + +#include + +namespace uLib { + class Object; + namespace Qt { class PropertyEditor; } +} + +class QVBoxLayout; +class QLabel; + +/** + * @class PropertiesPanel + * @brief A panel dedicated to inspecting and editing properties of a selected uLib::Object. + */ +class PropertiesPanel : public QWidget { + Q_OBJECT +public: + explicit PropertiesPanel(QWidget* parent = nullptr); + virtual ~PropertiesPanel(); + + /** @brief Sets the object to be inspected. */ + void setObject(uLib::Object* obj); + +private: + QVBoxLayout* m_layout; + QWidget* m_titleBar; + QLabel* m_titleLabel; + + uLib::Qt::PropertyEditor* m_editor; +}; + +#endif // PROPERTIES_PANEL_H diff --git a/app/gcompose/src/PropertyWidgets.cpp b/app/gcompose/src/PropertyWidgets.cpp new file mode 100644 index 0000000..0a14c0a --- /dev/null +++ b/app/gcompose/src/PropertyWidgets.cpp @@ -0,0 +1,173 @@ +#include "PropertyWidgets.h" +#include +#include "Vtk/uLibVtkInterface.h" + +namespace uLib { +namespace Qt { + +PropertyWidgetBase::PropertyWidgetBase(PropertyBase* prop, QWidget* parent) + : QWidget(parent), m_BaseProperty(prop) { + m_Layout = new QHBoxLayout(this); + m_Layout->setContentsMargins(4, 2, 4, 2); + m_Label = new QLabel(QString::fromStdString(prop->GetName()), this); + m_Label->setMinimumWidth(100); + m_Layout->addWidget(m_Label); +} +PropertyWidgetBase::~PropertyWidgetBase() {} + +DoublePropertyWidget::DoublePropertyWidget(Property* prop, QWidget* parent) + : PropertyWidgetBase(prop, parent), m_Prop(prop) { + m_SpinBox = new QDoubleSpinBox(this); + m_SpinBox->setRange(-1000000.0, 1000000.0); + m_SpinBox->setValue(prop->Get()); + m_Layout->addWidget(m_SpinBox, 1); + connect(m_SpinBox, static_cast(&QDoubleSpinBox::valueChanged), + [this](double val){ if (m_Prop->Get() != val) m_Prop->Set(val); }); + uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ + if (m_SpinBox->value() != m_Prop->Get()) { + QSignalBlocker blocker(m_SpinBox); + m_SpinBox->setValue(m_Prop->Get()); + } + }); +} +DoublePropertyWidget::~DoublePropertyWidget() {} + +FloatPropertyWidget::FloatPropertyWidget(Property* prop, QWidget* parent) + : PropertyWidgetBase(prop, parent), m_Prop(prop) { + m_SpinBox = new QDoubleSpinBox(this); + m_SpinBox->setRange(-1000000.0, 1000000.0); + m_SpinBox->setDecimals(3); + m_SpinBox->setValue(prop->Get()); + m_Layout->addWidget(m_SpinBox, 1); + connect(m_SpinBox, static_cast(&QDoubleSpinBox::valueChanged), + [this](double val){ if (m_Prop->Get() != (float)val) m_Prop->Set((float)val); }); + uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ + if (m_SpinBox->value() != (double)m_Prop->Get()) { + QSignalBlocker blocker(m_SpinBox); + m_SpinBox->setValue((double)m_Prop->Get()); + } + }); +} +FloatPropertyWidget::~FloatPropertyWidget() {} + +IntPropertyWidget::IntPropertyWidget(Property* prop, QWidget* parent) + : PropertyWidgetBase(prop, parent), m_Prop(prop) { + m_SpinBox = new QSpinBox(this); + m_SpinBox->setRange(-1000000, 1000000); + m_SpinBox->setValue(prop->Get()); + m_Layout->addWidget(m_SpinBox, 1); + connect(m_SpinBox, static_cast(&QSpinBox::valueChanged), + [this](int val){ if (m_Prop->Get() != val) m_Prop->Set(val); }); + uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ + if (m_SpinBox->value() != m_Prop->Get()) { + QSignalBlocker blocker(m_SpinBox); + m_SpinBox->setValue(m_Prop->Get()); + } + }); +} +IntPropertyWidget::~IntPropertyWidget() {} + +BoolPropertyWidget::BoolPropertyWidget(Property* prop, QWidget* parent) + : PropertyWidgetBase(prop, parent), m_Prop(prop) { + m_CheckBox = new QCheckBox(this); + m_CheckBox->setChecked(prop->Get()); + m_Layout->addWidget(m_CheckBox, 1); + connect(m_CheckBox, &QCheckBox::toggled, [this](bool val){ if (m_Prop->Get() != val) m_Prop->Set(val); }); + uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ + if (m_CheckBox->isChecked() != m_Prop->Get()) { + QSignalBlocker blocker(m_CheckBox); + m_CheckBox->setChecked(m_Prop->Get()); + } + }); +} +BoolPropertyWidget::~BoolPropertyWidget() {} + +StringPropertyWidget::StringPropertyWidget(Property* prop, QWidget* parent) + : PropertyWidgetBase(prop, parent), m_Prop(prop) { + m_LineEdit = new QLineEdit(this); + m_LineEdit->setText(QString::fromStdString(prop->Get())); + m_Layout->addWidget(m_LineEdit, 1); + connect(m_LineEdit, &QLineEdit::editingFinished, [this](){ + std::string val = m_LineEdit->text().toStdString(); + if (m_Prop->Get() != val) m_Prop->Set(val); + }); + uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ + if (m_LineEdit->text().toStdString() != m_Prop->Get()) { + QSignalBlocker blocker(m_LineEdit); + m_LineEdit->setText(QString::fromStdString(m_Prop->Get())); + } + }); +} +StringPropertyWidget::~StringPropertyWidget() {} + +PropertyEditor::PropertyEditor(QWidget* parent) : QWidget(parent), m_Object(nullptr) { + m_MainLayout = new QVBoxLayout(this); + m_MainLayout->setContentsMargins(0, 0, 0, 0); + m_ScrollArea = new QScrollArea(this); + m_ScrollArea->setWidgetResizable(true); + m_MainLayout->addWidget(m_ScrollArea); + m_Container = new QWidget(); + m_ContainerLayout = new QVBoxLayout(m_Container); + m_ContainerLayout->setAlignment(::Qt::AlignTop); + m_ScrollArea->setWidget(m_Container); + + registerFactory([](PropertyBase* p, QWidget* parent){ + return new DoublePropertyWidget(static_cast*>(p), parent); + }); + registerFactory([](PropertyBase* p, QWidget* parent){ + return new FloatPropertyWidget(static_cast*>(p), parent); + }); + registerFactory([](PropertyBase* p, QWidget* parent){ + return new IntPropertyWidget(static_cast*>(p), parent); + }); + registerFactory([](PropertyBase* p, QWidget* parent){ + return new BoolPropertyWidget(static_cast*>(p), parent); + }); + registerFactory([](PropertyBase* p, QWidget* parent){ + return new StringPropertyWidget(static_cast*>(p), parent); + }); +} + +PropertyEditor::~PropertyEditor() {} + +void PropertyEditor::setObject(::uLib::Object* obj, bool displayOnly) { + m_Object = obj; + clear(); + if (!obj) return; + + // Choose which properties to show + const std::vector<::uLib::PropertyBase*>* props = &obj->GetProperties(); + + if (displayOnly) { + if (auto* puppet = dynamic_cast<::uLib::Vtk::Puppet*>(obj)) { + props = &puppet->GetDisplayProperties(); + } else { + // If it's not a puppet but displayOnly is requested, showing nothing or fallback? + // Fallback: core properties. + } + } + + for (auto* prop : *props) { + auto it = m_Factories.find(prop->GetTypeIndex()); + if (it != m_Factories.end()) { + QWidget* widget = it->second(prop, m_Container); + m_ContainerLayout->addWidget(widget); + } else { + QWidget* fallback = new PropertyWidgetBase(prop, m_Container); + fallback->layout()->addWidget(new QLabel("(Read-only: " + QString::fromStdString(prop->GetValueAsString()) + ")")); + m_ContainerLayout->addWidget(fallback); + } + } + m_ContainerLayout->addStretch(1); +} + +void PropertyEditor::clear() { + QLayoutItem* item; + while ((item = m_ContainerLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } +} + +} // namespace Qt +} // namespace uLib diff --git a/app/gcompose/src/PropertyWidgets.h b/app/gcompose/src/PropertyWidgets.h new file mode 100644 index 0000000..47f17dc --- /dev/null +++ b/app/gcompose/src/PropertyWidgets.h @@ -0,0 +1,110 @@ +#ifndef PROPERTY_WIDGETS_H +#define PROPERTY_WIDGETS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Core/Property.h" +#include "Core/Object.h" + +namespace uLib { +namespace Qt { + +class PropertyWidgetBase : public QWidget { + Q_OBJECT +public: + PropertyWidgetBase(PropertyBase* prop, QWidget* parent = nullptr); + virtual ~PropertyWidgetBase(); + PropertyBase* getProperty() const { return m_BaseProperty; } + +protected: + PropertyBase* m_BaseProperty; + QHBoxLayout* m_Layout; + QLabel* m_Label; +}; + +class DoublePropertyWidget : public PropertyWidgetBase { + Q_OBJECT +public: + DoublePropertyWidget(Property* prop, QWidget* parent = nullptr); + virtual ~DoublePropertyWidget(); +private: + Property* m_Prop; + QDoubleSpinBox* m_SpinBox; +}; + +class FloatPropertyWidget : public PropertyWidgetBase { + Q_OBJECT +public: + FloatPropertyWidget(Property* prop, QWidget* parent = nullptr); + virtual ~FloatPropertyWidget(); +private: + Property* m_Prop; + QDoubleSpinBox* m_SpinBox; +}; + +class IntPropertyWidget : public PropertyWidgetBase { + Q_OBJECT +public: + IntPropertyWidget(Property* prop, QWidget* parent = nullptr); + virtual ~IntPropertyWidget(); +private: + Property* m_Prop; + QSpinBox* m_SpinBox; +}; + +class BoolPropertyWidget : public PropertyWidgetBase { + Q_OBJECT +public: + BoolPropertyWidget(Property* prop, QWidget* parent = nullptr); + virtual ~BoolPropertyWidget(); +private: + Property* m_Prop; + QCheckBox* m_CheckBox; +}; + +class StringPropertyWidget : public PropertyWidgetBase { + Q_OBJECT +public: + StringPropertyWidget(Property* prop, QWidget* parent = nullptr); + virtual ~StringPropertyWidget(); +private: + Property* m_Prop; + QLineEdit* m_LineEdit; +}; + +class PropertyEditor : public QWidget { + Q_OBJECT +public: + PropertyEditor(QWidget* parent = nullptr); + virtual ~PropertyEditor(); + void setObject(uLib::Object* obj, bool displayOnly = false); + template + void registerFactory(std::function factory) { + m_Factories[std::type_index(typeid(T))] = factory; + } + +private: + void clear(); + uLib::Object* m_Object; + QVBoxLayout* m_MainLayout; + QScrollArea* m_ScrollArea; + QWidget* m_Container; + QVBoxLayout* m_ContainerLayout; + std::map> m_Factories; +}; + +} // namespace Qt +} // namespace uLib + +#endif // PROPERTY_WIDGETS_H diff --git a/app/gcompose/src/QViewportPane.cpp b/app/gcompose/src/QViewportPane.cpp index b851564..876ebd1 100644 --- a/app/gcompose/src/QViewportPane.cpp +++ b/app/gcompose/src/QViewportPane.cpp @@ -9,6 +9,8 @@ #include #include #include +#include "PropertyWidgets.h" +#include QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullptr) { m_layout = new QVBoxLayout(this); @@ -21,11 +23,16 @@ QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(null m_titleBar->setFixedHeight(22); auto* titleLayout = new QHBoxLayout(m_titleBar); - titleLayout->setContentsMargins(5, 0, 5, 0); + titleLayout->setContentsMargins(5, 0, 0, 0); m_titleLabel = new QLabel("Viewport", m_titleBar); m_titleLabel->setObjectName("TitleLabel"); + m_toggleBtn = new QPushButton("Display", m_titleBar); + m_toggleBtn->setCheckable(true); + m_toggleBtn->setFixedSize(60, 18); + m_toggleBtn->setObjectName("DisplayToggleBtn"); + auto* closeBtn = new QToolButton(m_titleBar); closeBtn->setObjectName("PaneCloseButton"); closeBtn->setText("X"); @@ -33,17 +40,72 @@ QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(null titleLayout->addWidget(m_titleLabel); titleLayout->addStretch(); + titleLayout->addWidget(m_toggleBtn); + titleLayout->addSpacing(5); titleLayout->addWidget(closeBtn); m_layout->addWidget(m_titleBar); - m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu); + // Main horizontal container for viewport and display panel + QWidget* mainArea = new QWidget(this); + QHBoxLayout* hLayout = new QHBoxLayout(mainArea); + hLayout->setContentsMargins(0, 0, 0, 0); + hLayout->setSpacing(0); + m_layout->addWidget(mainArea); + + // Viewport will be added here via setViewport + m_viewport = new uLib::Vtk::QViewport(mainArea); + hLayout->addWidget(m_viewport); + + // Display Panel (Overlay/Slide-out) + m_displayPanel = new QFrame(mainArea); + m_displayPanel->setObjectName("DisplayPropertiesPanel"); + m_displayPanel->setFixedWidth(250); + m_displayPanel->hide(); + + QVBoxLayout* panelLayout = new QVBoxLayout(m_displayPanel); + panelLayout->setContentsMargins(5, 5, 5, 5); + + QLabel* panelHeader = new QLabel("Display Properties", m_displayPanel); + panelHeader->setStyleSheet("font-weight: bold; padding: 5px;"); + panelLayout->addWidget(panelHeader); + + m_displayEditor = new uLib::Qt::PropertyEditor(m_displayPanel); + panelLayout->addWidget(m_displayEditor); + + hLayout->addWidget(m_displayPanel); + + connect(m_toggleBtn, &QPushButton::toggled, this, &QViewportPane::toggleDisplayPanel); connect(m_titleBar, &QWidget::customContextMenuRequested, this, &QViewportPane::showContextMenu); connect(closeBtn, &QToolButton::clicked, this, &QViewportPane::onCloseRequested); - addVtkViewport(); // Initialize with a default VTK viewport + m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu); } +void QViewportPane::toggleDisplayPanel() { + m_displayPanel->setVisible(m_toggleBtn->isChecked()); +} + +void QViewportPane::setObject(uLib::Object* obj) { + m_displayEditor->setObject(obj, true); + // Auto-show panel if it's a puppet and we want to highlight this feature? + // User asked for "hiding panel", so maybe we don't auto-show. +} + +void QViewportPane::setViewport(QWidget* viewport, const QString& title) { + if (m_viewport) { + m_viewport->parentWidget()->layout()->removeWidget(m_viewport); + delete m_viewport; + } + m_viewport = viewport; + m_titleLabel->setText(title); + + m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + auto* mainAreaLayout = static_cast(m_displayPanel->parentWidget()->layout()); + mainAreaLayout->insertWidget(0, m_viewport); +} + + QViewportPane::~QViewportPane() {} void QViewportPane::setViewport(QWidget* viewport, const QString& title) { diff --git a/app/gcompose/src/QViewportPane.h b/app/gcompose/src/QViewportPane.h index d67791d..f02c88c 100644 --- a/app/gcompose/src/QViewportPane.h +++ b/app/gcompose/src/QViewportPane.h @@ -2,6 +2,13 @@ #define QVIEWPORTPANE_H #include +#include +#include + +namespace uLib { + class Object; + namespace Qt { class PropertyEditor; } +} class QVBoxLayout; class QLabel; @@ -16,10 +23,14 @@ public: void addRootCanvas(); QWidget* currentViewport() const { return m_viewport; } + + /** @brief Update the display properties for the given object. */ + void setObject(uLib::Object* obj); private slots: void onCloseRequested(); void showContextMenu(const QPoint& pos); + void toggleDisplayPanel(); private: void AttemptSplit(Qt::Orientation orientation); @@ -29,6 +40,11 @@ private: QWidget* m_titleBar; QLabel* m_titleLabel; QWidget* m_viewport; + + // Display Properties Overlay + QFrame* m_displayPanel; + uLib::Qt::PropertyEditor* m_displayEditor; + QPushButton* m_toggleBtn; }; #endif // QVIEWPORTPANE_H diff --git a/app/gcompose/src/StyleManager.cpp b/app/gcompose/src/StyleManager.cpp index 521e277..469836f 100644 --- a/app/gcompose/src/StyleManager.cpp +++ b/app/gcompose/src/StyleManager.cpp @@ -6,31 +6,83 @@ QWidget#MenuPanel { background-color: #2b2b2b; border-bottom: 1px solid #111; } QLabel#LogoLabel { font-weight: bold; color: #0078d7; font-size: 14px; letter-spacing: 1px; } QPushButton#MenuButton { background: transparent; color: #ccc; border: none; padding: 5px 10px; } QPushButton#MenuButton:hover { background: #3c3c3c; color: white; border-radius: 4px; } -QWidget#PaneTitleBar { background-color: #333; color: white; } +QWidget#PaneTitleBar { background-color: #333; color: white; border-bottom: 2px solid #222; } +QLabel#TitleLabel { font-weight: bold; margin-left: 2px; } QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #ccc; } -QToolButton#PaneCloseButton:hover { color: white; background: red; } +QToolButton#PaneCloseButton:hover { color: white; background: #c42b1c; } + +/* Viewport & Properties Panels */ +QWidget#DisplayPropertiesPanel, QWidget#PropertiesPanel { background-color: #252526; border-left: 1px solid #3e3e42; } +QPushButton#DisplayToggleBtn { background-color: #333337; border: 1px solid #3e3e42; border-radius: 2px; color: #f1f1f1; font-size: 11px; } +QPushButton#DisplayToggleBtn:checked { background-color: #0078d7; color: white; border-color: #005a9e; font-weight: bold; } +QPushButton#DisplayToggleBtn:hover { border-color: #0078d7; } + +QScrollArea { border: none; background: transparent; } +QScrollArea > QWidget > QWidget { background: transparent; } + +/* Property Widgets Styling */ +QLabel { color: #cccccc; } +QDoubleSpinBox, QSpinBox, QLineEdit { background: #3c3c3c; color: #f1f1f1; border: 1px solid #3e3e42; padding: 2px 4px; border-radius: 2px; selection-background-color: #0078d7; } +QDoubleSpinBox:focus, QSpinBox:focus, QLineEdit:focus { border-color: #0078d7; } +QCheckBox { color: #cccccc; spacing: 5px; } +QCheckBox::indicator { width: 14px; height: 14px; border: 1px solid #3e3e42; background: #333337; border-radius: 2px; } +QCheckBox::indicator:checked { background: #0078d7; border-color: #005a9e; } +QCheckBox::indicator:hover { border-color: #0078d7; } + QMenu { background-color: #2b2b2b; color: white; border: 1px solid #111; } QMenu::item:selected { background-color: #3c3c3c; } QTreeView#ContextTree { background-color: #1e1e1e; color: #ccc; border: none; } QTreeView#ContextTree::item:hover { background-color: #2a2d2e; } QTreeView#ContextTree::item:selected { background-color: #094771; color: white; } QHeaderView::section { background-color: #252526; color: #ccc; border: 1px solid #323233; padding: 4px; } + +/* ScrollBars */ +QScrollBar:vertical { background: #1e1e1e; width: 12px; margin: 0px; } +QScrollBar::handle:vertical { background: #3e3e42; min-height: 20px; border-radius: 6px; margin: 2px; } +QScrollBar::handle:vertical:hover { background: #505050; } +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; } )"; static const QString BRIGHT_THEME = R"( -QWidget#MenuPanel { background-color: #f0f0f0; border-bottom: 1px solid #ccc; } +QWidget#MenuPanel { background-color: #f3f3f3; border-bottom: 1px solid #ccc; } QLabel#LogoLabel { font-weight: bold; color: #005a9e; font-size: 14px; letter-spacing: 1px; } QPushButton#MenuButton { background: transparent; color: #333; border: none; padding: 5px 10px; } -QPushButton#MenuButton:hover { background: #d0d0d0; color: black; border-radius: 4px; } -QWidget#PaneTitleBar { background-color: #e0e0e0; color: black; } +QPushButton#MenuButton:hover { background: #e5e5e5; color: black; border-radius: 4px; } +QWidget#PaneTitleBar { background-color: #eeeeee; color: black; border-bottom: 2px solid #ddd; } +QLabel#TitleLabel { font-weight: bold; margin-left: 2px; } QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #666; } QToolButton#PaneCloseButton:hover { color: white; background: #e81123; } -QMenu { background-color: #f0f0f0; color: black; border: 1px solid #ccc; } + +/* Viewport & Properties Panels */ +QWidget#DisplayPropertiesPanel { background-color: #ffffff; border-left: 1px solid #cccccc; } +QPushButton#DisplayToggleBtn { background-color: #ffffff; border: 1px solid #cccccc; border-radius: 2px; color: #333; font-size: 11px; } +QPushButton#DisplayToggleBtn:checked { background-color: #0078d7; color: white; border-color: #005a9e; font-weight: bold; } +QPushButton#DisplayToggleBtn:hover { border-color: #0078d7; } + +QScrollArea { border: none; background: transparent; } +QScrollArea > QWidget > QWidget { background: transparent; } + +/* Property Widgets Styling */ +QLabel { color: #333333; } +QDoubleSpinBox, QSpinBox, QLineEdit { background: #ffffff; color: #333333; border: 1px solid #cccccc; padding: 2px 4px; border-radius: 2px; selection-background-color: #0078d7; } +QDoubleSpinBox:focus, QSpinBox:focus, QLineEdit:focus { border-color: #0078d7; } +QCheckBox { color: #333333; spacing: 5px; } +QCheckBox::indicator { width: 14px; height: 14px; border: 1px solid #cccccc; background: #ffffff; border-radius: 2px; } +QCheckBox::indicator:checked { background: #0078d7; border-color: #005a9e; } +QCheckBox::indicator:hover { border-color: #0078d7; } + +QMenu { background-color: #f3f3f3; color: black; border: 1px solid #ccc; } QMenu::item:selected { background-color: #d0d0d0; } QTreeView#ContextTree { background-color: #ffffff; color: #333; border: none; } QTreeView#ContextTree::item:hover { background-color: #f2f2f2; } QTreeView#ContextTree::item:selected { background-color: #0078d7; color: white; } QHeaderView::section { background-color: #f3f3f3; color: #333; border: 1px solid #ccc; padding: 4px; } + +/* ScrollBars */ +QScrollBar:vertical { background: #ffffff; width: 12px; margin: 0px; } +QScrollBar::handle:vertical { background: #cccccc; min-height: 20px; border-radius: 6px; margin: 2px; } +QScrollBar::handle:vertical:hover { background: #aaaaaa; } +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; } )"; void StyleManager::applyStyle(QApplication* app, const QString& themeName) { diff --git a/app/gcompose/src/ViewportPane.cpp b/app/gcompose/src/ViewportPane.cpp index d43c014..016d68b 100644 --- a/app/gcompose/src/ViewportPane.cpp +++ b/app/gcompose/src/ViewportPane.cpp @@ -9,6 +9,8 @@ #include #include #include +#include "PropertyWidgets.h" +#include ViewportPane::ViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullptr) { m_layout = new QVBoxLayout(this); @@ -21,11 +23,16 @@ ViewportPane::ViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullpt m_titleBar->setFixedHeight(22); auto* titleLayout = new QHBoxLayout(m_titleBar); - titleLayout->setContentsMargins(5, 0, 5, 0); + titleLayout->setContentsMargins(5, 0, 0, 0); m_titleLabel = new QLabel("Viewport", m_titleBar); m_titleLabel->setObjectName("TitleLabel"); + m_toggleBtn = new QPushButton("Display", m_titleBar); + m_toggleBtn->setCheckable(true); + m_toggleBtn->setFixedSize(60, 18); + m_toggleBtn->setObjectName("DisplayToggleBtn"); + auto* closeBtn = new QToolButton(m_titleBar); closeBtn->setObjectName("PaneCloseButton"); closeBtn->setText("X"); @@ -33,29 +40,82 @@ ViewportPane::ViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullpt titleLayout->addWidget(m_titleLabel); titleLayout->addStretch(); + titleLayout->addWidget(m_toggleBtn); + titleLayout->addSpacing(5); titleLayout->addWidget(closeBtn); m_layout->addWidget(m_titleBar); - m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu); + // Main horizontal container for viewport and display panel + QWidget* mainArea = new QWidget(this); + QHBoxLayout* hLayout = new QHBoxLayout(mainArea); + hLayout->setContentsMargins(0, 0, 0, 0); + hLayout->setSpacing(0); + m_layout->addWidget(mainArea); + + // Viewport will be added here via setViewport + m_viewport = new uLib::Vtk::QViewport(mainArea); + hLayout->addWidget(m_viewport); + + // Display Panel (Overlay/Slide-out) + m_displayPanel = new QFrame(mainArea); + m_displayPanel->setObjectName("DisplayPropertiesPanel"); + m_displayPanel->setFixedWidth(250); + m_displayPanel->hide(); + + QVBoxLayout* panelLayout = new QVBoxLayout(m_displayPanel); + panelLayout->setContentsMargins(5, 5, 5, 5); + + QLabel* panelHeader = new QLabel("Display Properties", m_displayPanel); + panelHeader->setStyleSheet("font-weight: bold; padding: 5px;"); + panelLayout->addWidget(panelHeader); + + m_displayEditor = new uLib::Qt::PropertyEditor(m_displayPanel); + panelLayout->addWidget(m_displayEditor); + + hLayout->addWidget(m_displayPanel); + + connect(m_toggleBtn, &QPushButton::toggled, this, &ViewportPane::toggleDisplayPanel); connect(m_titleBar, &QWidget::customContextMenuRequested, this, &ViewportPane::showContextMenu); connect(closeBtn, &QToolButton::clicked, this, &ViewportPane::onCloseRequested); - addVtkViewport(); // Initialize with a default VTK viewport + m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu); } ViewportPane::~ViewportPane() {} +void ViewportPane::toggleDisplayPanel() { + m_displayPanel->setVisible(m_toggleBtn->isChecked()); +} + +void ViewportPane::setObject(uLib::Object* obj) { + m_displayEditor->setObject(obj, true); + + // Check if the object is a Puppet (meaning it has display properties) + bool isPuppet = (dynamic_cast<::uLib::Vtk::Puppet*>(obj) != nullptr); + + // Only show the "Display" toggle button if it's a puppet + m_toggleBtn->setVisible(isPuppet); + + // If it's a puppet, we might want to keep the panel state if it was already open, + // or if it's NOT a puppet, definitely hide the toggle and panel. + if (!isPuppet) { + m_toggleBtn->setChecked(false); + m_displayPanel->hide(); + } +} + void ViewportPane::setViewport(QWidget* viewport, const QString& title) { if (m_viewport) { - m_layout->removeWidget(m_viewport); + m_viewport->parentWidget()->layout()->removeWidget(m_viewport); delete m_viewport; } m_viewport = viewport; m_titleLabel->setText(title); m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_layout->addWidget(m_viewport); + auto* mainAreaLayout = static_cast(m_displayPanel->parentWidget()->layout()); + mainAreaLayout->insertWidget(0, m_viewport); } void ViewportPane::addVtkViewport() { @@ -73,7 +133,6 @@ void ViewportPane::onCloseRequested() { if (parentSplitter && parentSplitter->count() > 1) { deleteLater(); } else { - // Can't close the last viewport in the splitter safely. Re-initialize to default VTK canvas. addVtkViewport(); } } @@ -83,64 +142,20 @@ void ViewportPane::showContextMenu(const QPoint& pos) { QAction* hSplit = menu.addAction("H split"); QAction* vSplit = menu.addAction("V split"); menu.addSeparator(); - bool isVtk = (qobject_cast(m_viewport) != nullptr); - QAction* changeType = menu.addAction(isVtk ? "Change to ROOT Canvas" : "Change to VTK Viewport"); - QAction* selected = menu.exec(m_titleBar->mapToGlobal(pos)); - - if (selected == hSplit) { - AttemptSplit(Qt::Horizontal); - } else if (selected == vSplit) { - AttemptSplit(Qt::Vertical); - } else if (selected == changeType) { - if (isVtk) { - addRootCanvas(); - } else { - addVtkViewport(); - } - } + if (selected == hSplit) AttemptSplit(Qt::Horizontal); + else if (selected == vSplit) AttemptSplit(Qt::Vertical); + else if (selected == changeType) isVtk ? addRootCanvas() : addVtkViewport(); } void ViewportPane::AttemptSplit(Qt::Orientation orientation) { QWidget* p = parentWidget(); if (!p) return; - QSplitter* parentSplitter = qobject_cast(p); if (!parentSplitter) return; - ViewportPane* newPane = new ViewportPane(); - - // 1. Synchronize viewport content and camera (VTK Viewport only for now) - auto* currentVtk = qobject_cast(m_viewport); - if (currentVtk) { - auto* newVtk = qobject_cast(newPane->currentViewport()); - if (newVtk) { - // Copy puppets - for (auto* puppet : currentVtk->getPuppets()) { - newVtk->AddPuppet(*puppet); - } - // Copy camera - if (currentVtk->GetRenderer() && newVtk->GetRenderer()) { - vtkCamera* currentCam = currentVtk->GetRenderer()->GetActiveCamera(); - vtkCamera* newCam = newVtk->GetRenderer()->GetActiveCamera(); - if (currentCam && newCam) { - newCam->DeepCopy(currentCam); - } - } - // Sync grid visible and axis - newVtk->SetGridVisible(currentVtk->GetGridVisible()); - newVtk->SetGridAxis(currentVtk->GetGridAxis()); - } - } - - // 2. Adjust for ROOT Canvas if that was the active view - bool isRoot = (qobject_cast(m_viewport) != nullptr); - if (isRoot) { - newPane->addRootCanvas(); - } - if (parentSplitter->orientation() == orientation) { int index = parentSplitter->indexOf(this); QList sizes = parentSplitter->sizes(); @@ -148,21 +163,17 @@ void ViewportPane::AttemptSplit(Qt::Orientation orientation) { int half = currentSize / 2; sizes[index] = half; sizes.insert(index + 1, currentSize - half); - parentSplitter->insertWidget(index + 1, newPane); parentSplitter->setSizes(sizes); } else { int index = parentSplitter->indexOf(this); QList parentSizes = parentSplitter->sizes(); - QSplitter* newSplitter = new QSplitter(orientation); newSplitter->addWidget(this); newSplitter->addWidget(newPane); - QList subSizes; subSizes << 500 << 500; newSplitter->setSizes(subSizes); - parentSplitter->insertWidget(index, newSplitter); parentSplitter->setSizes(parentSizes); } diff --git a/app/gcompose/src/ViewportPane.h b/app/gcompose/src/ViewportPane.h index 9207dad..0c74845 100644 --- a/app/gcompose/src/ViewportPane.h +++ b/app/gcompose/src/ViewportPane.h @@ -2,6 +2,13 @@ #define VIEWPORTPANE_H #include +#include +#include + +namespace uLib { + class Object; + namespace Qt { class PropertyEditor; } +} class QVBoxLayout; class QLabel; @@ -16,10 +23,14 @@ public: void addRootCanvas(); QWidget* currentViewport() const { return m_viewport; } + + /** @brief Update the display properties for the given object. */ + void setObject(uLib::Object* obj); private slots: void onCloseRequested(); void showContextMenu(const QPoint& pos); + void toggleDisplayPanel(); private: void AttemptSplit(Qt::Orientation orientation); @@ -29,6 +40,11 @@ private: QWidget* m_titleBar; QLabel* m_titleLabel; QWidget* m_viewport; + + // Display Properties Overlay + QFrame* m_displayPanel; + uLib::Qt::PropertyEditor* m_displayEditor; + QPushButton* m_toggleBtn; }; #endif // VIEWPORTPANE_H diff --git a/src/Core/Object.h b/src/Core/Object.h index ae73a31..4b784c2 100644 --- a/src/Core/Object.h +++ b/src/Core/Object.h @@ -99,6 +99,15 @@ public: template void serialize(ArchiveT &ar, const unsigned int version); + + virtual void serialize(Archive::xml_oarchive & ar, const unsigned int version) {} + virtual void serialize(Archive::xml_iarchive & ar, const unsigned int version) {} + virtual void serialize(Archive::text_oarchive & ar, const unsigned int version) {} + virtual void serialize(Archive::text_iarchive & ar, const unsigned int version) {} + virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) {} + virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) {} + virtual void serialize(Archive::log_archive & ar, const unsigned int version) {} + template void save_override(ArchiveT &ar, const unsigned int version); diff --git a/src/Core/Property.h b/src/Core/Property.h index 9194fa7..7363699 100644 --- a/src/Core/Property.h +++ b/src/Core/Property.h @@ -4,6 +4,7 @@ #include #include #include +#include // Added #include #include #include "Core/Archives.h" @@ -21,6 +22,7 @@ public: virtual const std::string& GetName() const = 0; virtual const char* GetTypeName() const = 0; virtual std::string GetValueAsString() const = 0; + virtual std::type_index GetTypeIndex() const = 0; // Added // Signal support signals: @@ -62,8 +64,11 @@ public: if (m_own) delete m_value; } - const std::string& GetName() const override { return m_name; } - const char* GetTypeName() const override { return typeid(T).name(); } + // Identification + virtual const std::string& GetName() const override { return m_name; } + virtual const char* GetTypeName() const override { return typeid(T).name(); } + virtual std::type_index GetTypeIndex() const override { return std::type_index(typeid(T)); } + std::string GetValueAsString() const override { try { @@ -191,7 +196,7 @@ public: // Handle standard NVPs by recursing (important for base classes) template void save_override(const boost::serialization::nvp &t) { - this->detail_common_oarchive::save_override(t.const_value()); + boost::archive::detail::common_oarchive::save_override(t.const_value()); } // Ignore everything else diff --git a/src/Vtk/HEP/MuonTomography/vtkVoxRaytracerRepresentation.h b/src/Vtk/HEP/MuonTomography/vtkVoxRaytracerRepresentation.h index 7f53034..2e7bab9 100644 --- a/src/Vtk/HEP/MuonTomography/vtkVoxRaytracerRepresentation.h +++ b/src/Vtk/HEP/MuonTomography/vtkVoxRaytracerRepresentation.h @@ -58,7 +58,7 @@ class vtkActor; namespace uLib { namespace Vtk { -class vtkVoxRaytracerRepresentation : public Puppet, public Object { +class vtkVoxRaytracerRepresentation : public Puppet { typedef VoxRaytracer Content; public: diff --git a/src/Vtk/uLibVtkInterface.cxx b/src/Vtk/uLibVtkInterface.cxx index b904bb4..c2e76d0 100644 --- a/src/Vtk/uLibVtkInterface.cxx +++ b/src/Vtk/uLibVtkInterface.cxx @@ -199,8 +199,8 @@ public: Puppet::Puppet() : Object(), d(new PuppetData) { - ULIB_ACTIVATE_PROPERTIES(*this); - for (auto* p : this->GetProperties()) { + ULIB_ACTIVATE_DISPLAY_PROPERTIES; + for (auto* p : this->GetDisplayProperties()) { uLib::Object::connect(p, &uLib::PropertyBase::Updated, this, &Puppet::Update); } } @@ -235,6 +235,11 @@ void Puppet::RemoveProp(vtkProp *prop) } +vtkPropCollection *Puppet::GetParts() +{ + return d->m_Assembly->GetParts(); +} + vtkPropCollection *Puppet::GetProps() { return d->m_Assembly->GetParts(); @@ -474,8 +479,7 @@ void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor) { } -template -void Puppet::serialize(Archive & ar, const unsigned int version) { +void Puppet::serialize_display(Archive::display_properties_archive & ar, const unsigned int version) { ar & boost::serialization::make_hrp("ColorR", d->m_Color[0]); ar & boost::serialization::make_hrp("ColorG", d->m_Color[1]); ar & boost::serialization::make_hrp("ColorB", d->m_Color[2]); @@ -483,10 +487,13 @@ void Puppet::serialize(Archive & ar, const unsigned int version) { ar & boost::serialization::make_hrp("Representation", d->m_Representation); } -void Puppet::save_override(Archive::xml_oarchive & ar, const unsigned int v) { serialize(ar, v); } -void Puppet::save_override(Archive::hrt_oarchive & ar, const unsigned int v) { serialize(ar, v); } -void Puppet::save_override(Archive::log_archive & ar, const unsigned int v) { serialize(ar, v); } -void Puppet::save_override(Archive::text_oarchive & ar, const unsigned int v) { serialize(ar, v); } +void Puppet::serialize(Archive::xml_oarchive & ar, const unsigned int v) { } +void Puppet::serialize(Archive::xml_iarchive & ar, const unsigned int v) { } +void Puppet::serialize(Archive::text_oarchive & ar, const unsigned int v) { } +void Puppet::serialize(Archive::text_iarchive & ar, const unsigned int v) { } +void Puppet::serialize(Archive::hrt_oarchive & ar, const unsigned int v) { } +void Puppet::serialize(Archive::hrt_iarchive & ar, const unsigned int v) { } +void Puppet::serialize(Archive::log_archive & ar, const unsigned int v) { } } // namespace Vtk } // namespace uLib diff --git a/src/Vtk/uLibVtkInterface.h b/src/Vtk/uLibVtkInterface.h index 1bed7d3..619564c 100644 --- a/src/Vtk/uLibVtkInterface.h +++ b/src/Vtk/uLibVtkInterface.h @@ -28,7 +28,9 @@ #include #include +#include #include "Core/Object.h" +#include "Core/Property.h" // vtk classes forward declaration // class vtkProp; @@ -38,6 +40,10 @@ class vtkRenderer; class vtkRendererCollection; class vtkRenderWindowInteractor; +namespace uLib { + namespace Archive { class display_properties_archive; } + namespace Vtk { class Puppet; class Viewer; } +} namespace uLib { namespace Vtk { @@ -46,37 +52,21 @@ class Puppet : public uLib::Object { uLibTypeMacro(Puppet, uLib::Object) public: Puppet(); - ~Puppet(); + virtual ~Puppet(); virtual vtkProp *GetProp(); + virtual vtkPropCollection *GetParts(); + virtual vtkPropCollection *GetProps(); void ConnectRenderer(vtkRenderer *renderer); void DisconnectRenderer(vtkRenderer *renderer); - void ConnectViewer(class Viewer *viewer); + void ConnectViewer(Viewer *viewer); - void DisonnectViewer(class Viewer *viewer); - - vtkRendererCollection *GetRenderers() const; - - enum Representation { - Points, - Wireframe, - Surface - }; - - virtual void PrintSelf(std::ostream &o) const; - - void ShowBoundingBox(bool show = true); - - void ShowScaleMeasures(bool show = true); - - void SetRepresentation(Representation mode); - - void SetRepresentation(const char *mode); + void DisonnectViewer(Viewer *viewer); void SetColor(double r, double g, double b); @@ -90,21 +80,39 @@ public: virtual void Update(); + enum Representation { Points, Wireframe, Surface }; + void SetRepresentation(Representation mode); + void SetRepresentation(const char *mode); + + virtual void PrintSelf(std::ostream &o) const; + + void ShowBoundingBox(bool show); + void ShowScaleMeasures(bool show); + + vtkRendererCollection *GetRenderers() const; + + virtual void serialize(Archive::xml_oarchive & ar, const unsigned int version) override; + virtual void serialize(Archive::xml_iarchive & ar, const unsigned int version) override; + virtual void serialize(Archive::text_oarchive & ar, const unsigned int version) override; + virtual void serialize(Archive::text_iarchive & ar, const unsigned int version) override; + virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) override; + virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) override; + virtual void serialize(Archive::log_archive & ar, const unsigned int version) override; + + const std::vector& GetDisplayProperties() const { return m_DisplayProperties; } + void RegisterDisplayProperty(uLib::PropertyBase* prop) { m_DisplayProperties.push_back(prop); } + + virtual void serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version = 0); + virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor); - // Serialization and Reflection - template - void serialize(Archive & ar, const unsigned int version); - void save_override(Archive::xml_oarchive & ar, const unsigned int version); - void save_override(Archive::hrt_oarchive & ar, const unsigned int version); - void save_override(Archive::log_archive & ar, const unsigned int version); - void save_override(Archive::text_oarchive & ar, const unsigned int version); - protected: void SetProp(vtkProp *prop); void RemoveProp(vtkProp *prop); + std::vector m_DisplayProperties; + private: Puppet(const Puppet&) = delete; Puppet& operator=(const Puppet&) = delete; @@ -113,11 +121,60 @@ private: class PuppetData *d; }; - - - - } // namespace Vtk } // namespace uLib +// -------------------------------------------------------------------------- // + +namespace uLib { +namespace Archive { + +/** + * @brief Specialized archive for registering display-only properties in Puppets. + */ +class display_properties_archive : public boost::archive::detail::common_oarchive { +public: + display_properties_archive(Vtk::Puppet* puppet) : + boost::archive::detail::common_oarchive(boost::archive::no_header), + m_Puppet(puppet) {} + + template + void save_override(const boost::serialization::hrp &t) { + if (m_Puppet) { + m_Puppet->RegisterDisplayProperty( + new uLib::Property(m_Puppet, t.name(), &const_cast&>(t).value()) + ); + } + } + + template void save_override(const boost::serialization::nvp &t) { + boost::archive::detail::common_oarchive::save_override(t.const_value()); + } + + template void save_override(const T &t) {} + + void save_override(const boost::archive::object_id_type & t) {} + void save_override(const boost::archive::object_reference_type & t) {} + void save_override(const boost::archive::version_type & t) {} + void save_override(const boost::archive::class_id_type & t) {} + void save_override(const boost::archive::class_id_optional_type & t) {} + void save_override(const boost::archive::class_id_reference_type & t) {} + void save_override(const boost::archive::class_name_type & t) {} + void save_override(const boost::archive::tracking_type & t) {} + +private: + Vtk::Puppet* m_Puppet; +}; + +} // namespace Archive + +// This macro MUST be defined after both Puppet and display_properties_archive are fully defined. +#define ULIB_ACTIVATE_DISPLAY_PROPERTIES \ + { \ + uLib::Archive::display_properties_archive dar(this); \ + this->serialize_display(dar, 0); \ + } + +} // namespace uLib + #endif // ULIBVTKINTERFACE_H