#include "PropertyWidgets.h" #include #include #include #include "Vtk/uLibVtkInterface.h" #include "Math/Units.h" #include "Math/Dense.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() {} // Helper for unit parsing static double parseWithUnits(const QString& text, double* factorOut = nullptr, QString* suffixOut = nullptr) { static QRegularExpression re("^\\s*([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)\\s*(_?[a-zA-Z]+)?\\s*$"); QRegularExpressionMatch match = re.match(text); if (!match.hasMatch()) return 0.0; double num = match.captured(1).toDouble(); QString unit = match.captured(3); double factor = 1.0; if (!unit.isEmpty()) { QString u = unit.startsWith('_') ? unit.mid(1) : unit; if (u == "m") factor = CLHEP::meter; else if (u == "cm") factor = CLHEP::centimeter; else if (u == "mm") factor = CLHEP::millimeter; else if (u == "um") factor = CLHEP::micrometer; else if (u == "nm") factor = CLHEP::nanometer; else if (u == "km") factor = CLHEP::kilometer; else if (u == "deg") factor = CLHEP::degree; else if (u == "rad") factor = CLHEP::radian; else if (u == "ns") factor = CLHEP::nanosecond; else if (u == "s") factor = CLHEP::second; else if (u == "ms") factor = CLHEP::millisecond; else if (u == "MeV") factor = CLHEP::megaelectronvolt; else if (u == "eV") factor = CLHEP::electronvolt; else if (u == "keV") factor = CLHEP::kiloelectronvolt; else if (u == "GeV") factor = CLHEP::gigaelectronvolt; else if (u == "TeV") factor = CLHEP::teraelectronvolt; if (suffixOut) *suffixOut = u; } else if (suffixOut) { // Reuse previous suffix if none provided, or empty } if (factorOut) *factorOut = factor; return num * factor; } // UnitLineEdit implementation UnitLineEdit::UnitLineEdit(QWidget* parent) : QLineEdit(parent), m_Value(0), m_Factor(1.0), m_Suffix("mm"), m_IsInteger(false) { connect(this, &QLineEdit::editingFinished, this, &UnitLineEdit::onEditingFinished); } void UnitLineEdit::setValue(double val) { if (m_Value != val) { m_Value = val; // Initial heuristic for unit if it was mm and value becomes large if (!m_IsInteger && m_Suffix == "mm" && std::abs(val) >= 1000.0) { m_Suffix = "m"; m_Factor = CLHEP::meter; } updateText(); } } void UnitLineEdit::onEditingFinished() { double factor = m_Factor; QString suffix = m_Suffix; double parsedVal = parseWithUnits(text(), &factor, &suffix); if (!suffix.isEmpty()) { m_Suffix = suffix; m_Factor = factor; } if (m_IsInteger) { parsedVal = std::round(parsedVal); } if (m_Value != parsedVal) { m_Value = parsedVal; emit valueManualChanged(m_Value); } updateText(); } void UnitLineEdit::updateText() { QSignalBlocker blocker(this); if (m_IsInteger) { setText(QString::number((int)m_Value)); } else { double displayVal = m_Value / m_Factor; setText(QString::number(displayVal, 'g', 6) + " " + m_Suffix); } } void UnitLineEdit::setIntegerOnly(bool integerOnly) { m_IsInteger = integerOnly; updateText(); } DoublePropertyWidget::DoublePropertyWidget(Property* prop, QWidget* parent) : PropertyWidgetBase(prop, parent), m_Prop(prop) { m_Edit = new UnitLineEdit(this); m_Layout->addWidget(m_Edit, 1); m_Edit->setValue(prop->Get()); connect(m_Edit, &UnitLineEdit::valueManualChanged, [this](double val){ m_Prop->Set(val); }); uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ m_Edit->setValue(m_Prop->Get()); }); } FloatPropertyWidget::FloatPropertyWidget(Property* prop, QWidget* parent) : PropertyWidgetBase(prop, parent), m_Prop(prop) { m_Edit = new UnitLineEdit(this); m_Layout->addWidget(m_Edit, 1); m_Edit->setValue(prop->Get()); connect(m_Edit, &UnitLineEdit::valueManualChanged, [this](double val){ m_Prop->Set((float)val); }); uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ m_Edit->setValue((double)m_Prop->Get()); }); } IntPropertyWidget::IntPropertyWidget(Property* prop, QWidget* parent) : PropertyWidgetBase(prop, parent), m_Prop(prop) { m_Edit = new UnitLineEdit(this); m_Edit->setIntegerOnly(true); m_Layout->addWidget(m_Edit, 1); m_Edit->setValue(prop->Get()); connect(m_Edit, &UnitLineEdit::valueManualChanged, [this](double val){ m_Prop->Set((int)val); }); uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ m_Edit->setValue((double)m_Prop->Get()); }); } 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); }); // Vector Registration registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(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