vtkProperties

This commit is contained in:
AndreaRigoni
2026-03-23 17:46:42 +00:00
parent 5d0efb3078
commit f13342ff30
19 changed files with 745 additions and 186 deletions

View File

@@ -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

View File

@@ -1,5 +1,7 @@
#include "ContextPanel.h"
#include "ContextModel.h"
#include "PropertyWidgets.h"
#include "PropertiesPanel.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
@@ -8,12 +10,10 @@
#include <QList>
#include <QShortcut>
#include <QItemSelectionModel>
#include <Vtk/vtkQViewport.h>
#include <Vtk/vtkObjectsContext.h>
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<int> 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<uLib::Object*> 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);
}

View File

@@ -1,50 +1,43 @@
#ifndef CONTEXT_PANEL_H
#define CONTEXT_PANEL_H
#ifndef CONTEXTPANEL_H
#define CONTEXTPANEL_H
#include <QWidget>
#include <QItemSelection>
#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

View File

@@ -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<uLib::Vtk::QViewport*>(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<int> sizes;
sizes << 200 << 1000;
sizes << 250 << 600 << 250;
m_rootSplitter->setSizes(sizes);
mainLayout->addWidget(m_rootSplitter, 1);

View File

@@ -6,6 +6,7 @@
class QSplitter;
class ViewportPane;
class ContextPanel;
class PropertiesPanel;
namespace uLib {
class ObjectsContext;

View File

@@ -0,0 +1,45 @@
#include "PropertiesPanel.h"
#include "PropertyWidgets.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#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() {}

View File

@@ -0,0 +1,35 @@
#ifndef PROPERTIES_PANEL_H
#define PROPERTIES_PANEL_H
#include <QWidget>
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

View File

@@ -0,0 +1,173 @@
#include "PropertyWidgets.h"
#include <QSignalBlocker>
#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<double>* 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<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
[this](double val){ if (m_Prop->Get() != val) m_Prop->Set(val); });
uLib::Object::connect(m_Prop, &Property<double>::PropertyChanged, [this](){
if (m_SpinBox->value() != m_Prop->Get()) {
QSignalBlocker blocker(m_SpinBox);
m_SpinBox->setValue(m_Prop->Get());
}
});
}
DoublePropertyWidget::~DoublePropertyWidget() {}
FloatPropertyWidget::FloatPropertyWidget(Property<float>* 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<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
[this](double val){ if (m_Prop->Get() != (float)val) m_Prop->Set((float)val); });
uLib::Object::connect(m_Prop, &Property<float>::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<int>* 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<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
[this](int val){ if (m_Prop->Get() != val) m_Prop->Set(val); });
uLib::Object::connect(m_Prop, &Property<int>::PropertyChanged, [this](){
if (m_SpinBox->value() != m_Prop->Get()) {
QSignalBlocker blocker(m_SpinBox);
m_SpinBox->setValue(m_Prop->Get());
}
});
}
IntPropertyWidget::~IntPropertyWidget() {}
BoolPropertyWidget::BoolPropertyWidget(Property<bool>* 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<bool>::PropertyChanged, [this](){
if (m_CheckBox->isChecked() != m_Prop->Get()) {
QSignalBlocker blocker(m_CheckBox);
m_CheckBox->setChecked(m_Prop->Get());
}
});
}
BoolPropertyWidget::~BoolPropertyWidget() {}
StringPropertyWidget::StringPropertyWidget(Property<std::string>* 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<std::string>::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<double>([](PropertyBase* p, QWidget* parent){
return new DoublePropertyWidget(static_cast<Property<double>*>(p), parent);
});
registerFactory<float>([](PropertyBase* p, QWidget* parent){
return new FloatPropertyWidget(static_cast<Property<float>*>(p), parent);
});
registerFactory<int>([](PropertyBase* p, QWidget* parent){
return new IntPropertyWidget(static_cast<Property<int>*>(p), parent);
});
registerFactory<bool>([](PropertyBase* p, QWidget* parent){
return new BoolPropertyWidget(static_cast<Property<bool>*>(p), parent);
});
registerFactory<std::string>([](PropertyBase* p, QWidget* parent){
return new StringPropertyWidget(static_cast<Property<std::string>*>(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

View File

@@ -0,0 +1,110 @@
#ifndef PROPERTY_WIDGETS_H
#define PROPERTY_WIDGETS_H
#include <QWidget>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include <QLineEdit>
#include <QCheckBox>
#include <QScrollArea>
#include <map>
#include <typeindex>
#include <functional>
#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<double>* prop, QWidget* parent = nullptr);
virtual ~DoublePropertyWidget();
private:
Property<double>* m_Prop;
QDoubleSpinBox* m_SpinBox;
};
class FloatPropertyWidget : public PropertyWidgetBase {
Q_OBJECT
public:
FloatPropertyWidget(Property<float>* prop, QWidget* parent = nullptr);
virtual ~FloatPropertyWidget();
private:
Property<float>* m_Prop;
QDoubleSpinBox* m_SpinBox;
};
class IntPropertyWidget : public PropertyWidgetBase {
Q_OBJECT
public:
IntPropertyWidget(Property<int>* prop, QWidget* parent = nullptr);
virtual ~IntPropertyWidget();
private:
Property<int>* m_Prop;
QSpinBox* m_SpinBox;
};
class BoolPropertyWidget : public PropertyWidgetBase {
Q_OBJECT
public:
BoolPropertyWidget(Property<bool>* prop, QWidget* parent = nullptr);
virtual ~BoolPropertyWidget();
private:
Property<bool>* m_Prop;
QCheckBox* m_CheckBox;
};
class StringPropertyWidget : public PropertyWidgetBase {
Q_OBJECT
public:
StringPropertyWidget(Property<std::string>* prop, QWidget* parent = nullptr);
virtual ~StringPropertyWidget();
private:
Property<std::string>* 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<typename T>
void registerFactory(std::function<QWidget*(PropertyBase*, QWidget*)> 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<std::type_index, std::function<QWidget*(PropertyBase*, QWidget*)>> m_Factories;
};
} // namespace Qt
} // namespace uLib
#endif // PROPERTY_WIDGETS_H

View File

@@ -9,6 +9,8 @@
#include <QAction>
#include <QSplitter>
#include <vtkCamera.h>
#include "PropertyWidgets.h"
#include <QSignalBlocker>
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<QHBoxLayout*>(m_displayPanel->parentWidget()->layout());
mainAreaLayout->insertWidget(0, m_viewport);
}
QViewportPane::~QViewportPane() {}
void QViewportPane::setViewport(QWidget* viewport, const QString& title) {

View File

@@ -2,6 +2,13 @@
#define QVIEWPORTPANE_H
#include <QWidget>
#include <QFrame>
#include <QPushButton>
namespace uLib {
class Object;
namespace Qt { class PropertyEditor; }
}
class QVBoxLayout;
class QLabel;
@@ -17,9 +24,13 @@ public:
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

View File

@@ -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) {

View File

@@ -9,6 +9,8 @@
#include <QAction>
#include <QSplitter>
#include <vtkCamera.h>
#include "PropertyWidgets.h"
#include <QSignalBlocker>
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<QHBoxLayout*>(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<uLib::Vtk::QViewport*>(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<QSplitter*>(p);
if (!parentSplitter) return;
ViewportPane* newPane = new ViewportPane();
// 1. Synchronize viewport content and camera (VTK Viewport only for now)
auto* currentVtk = qobject_cast<uLib::Vtk::QViewport*>(m_viewport);
if (currentVtk) {
auto* newVtk = qobject_cast<uLib::Vtk::QViewport*>(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<uLib::Root::QCanvas*>(m_viewport) != nullptr);
if (isRoot) {
newPane->addRootCanvas();
}
if (parentSplitter->orientation() == orientation) {
int index = parentSplitter->indexOf(this);
QList<int> 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<int> parentSizes = parentSplitter->sizes();
QSplitter* newSplitter = new QSplitter(orientation);
newSplitter->addWidget(this);
newSplitter->addWidget(newPane);
QList<int> subSizes;
subSizes << 500 << 500;
newSplitter->setSizes(subSizes);
parentSplitter->insertWidget(index, newSplitter);
parentSplitter->setSizes(parentSizes);
}

View File

@@ -2,6 +2,13 @@
#define VIEWPORTPANE_H
#include <QWidget>
#include <QFrame>
#include <QPushButton>
namespace uLib {
class Object;
namespace Qt { class PropertyEditor; }
}
class QVBoxLayout;
class QLabel;
@@ -17,9 +24,13 @@ public:
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

View File

@@ -99,6 +99,15 @@ public:
template <class ArchiveT>
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 <class ArchiveT>
void save_override(ArchiveT &ar, const unsigned int version);

View File

@@ -4,6 +4,7 @@
#include <string>
#include <sstream>
#include <typeinfo>
#include <typeindex> // Added
#include <boost/serialization/nvp.hpp>
#include <boost/lexical_cast.hpp>
#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<class T>
void save_override(const boost::serialization::nvp<T> &t) {
this->detail_common_oarchive::save_override(t.const_value());
boost::archive::detail::common_oarchive<property_register_archive>::save_override(t.const_value());
}
// Ignore everything else

View File

@@ -58,7 +58,7 @@ class vtkActor;
namespace uLib {
namespace Vtk {
class vtkVoxRaytracerRepresentation : public Puppet, public Object {
class vtkVoxRaytracerRepresentation : public Puppet {
typedef VoxRaytracer Content;
public:

View File

@@ -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 <class Archive>
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

View File

@@ -28,7 +28,9 @@
#include <iomanip>
#include <ostream>
#include <vector>
#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();
virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor);
enum Representation { Points, Wireframe, Surface };
void SetRepresentation(Representation mode);
void SetRepresentation(const char *mode);
// Serialization and Reflection
template<class Archive>
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);
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<uLib::PropertyBase*>& 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);
protected:
void SetProp(vtkProp *prop);
void RemoveProp(vtkProp *prop);
std::vector<uLib::PropertyBase*> 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<display_properties_archive> {
public:
display_properties_archive(Vtk::Puppet* puppet) :
boost::archive::detail::common_oarchive<display_properties_archive>(boost::archive::no_header),
m_Puppet(puppet) {}
template<class T>
void save_override(const boost::serialization::hrp<T> &t) {
if (m_Puppet) {
m_Puppet->RegisterDisplayProperty(
new uLib::Property<T>(m_Puppet, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value())
);
}
}
template<class T> void save_override(const boost::serialization::nvp<T> &t) {
boost::archive::detail::common_oarchive<display_properties_archive>::save_override(t.const_value());
}
template<class T> 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