262 lines
7.0 KiB
C++
262 lines
7.0 KiB
C++
#ifndef PROPERTY_WIDGETS_H
|
|
#define PROPERTY_WIDGETS_H
|
|
|
|
#include <QWidget>
|
|
class QPushButton;
|
|
class QSlider;
|
|
class QComboBox;
|
|
#include <QLabel>
|
|
#include <QHBoxLayout>
|
|
#include <QVBoxLayout>
|
|
#include <QLineEdit>
|
|
#include <QCheckBox>
|
|
#include <QScrollArea>
|
|
#include <map>
|
|
#include <typeindex>
|
|
#include <functional>
|
|
|
|
#include "Core/Property.h"
|
|
#include "Core/Object.h"
|
|
#include "Core/Signal.h"
|
|
#include "Core/FontConfig.h"
|
|
#include "Math/Dense.h"
|
|
#include "Settings.h"
|
|
|
|
namespace uLib { class ObjectsContext; }
|
|
|
|
namespace uLib {
|
|
namespace Qt {
|
|
|
|
double parseWithUnits(const QString& text, double* factorOut = nullptr, QString* suffixOut = nullptr);
|
|
|
|
class PropertyWidgetBase : public QWidget {
|
|
Q_OBJECT
|
|
public:
|
|
PropertyWidgetBase(PropertyBase* prop, QWidget* parent = nullptr);
|
|
virtual ~PropertyWidgetBase();
|
|
PropertyBase* getProperty() const { return m_BaseProperty; }
|
|
|
|
signals:
|
|
void updated();
|
|
|
|
protected:
|
|
PropertyBase* m_BaseProperty;
|
|
QHBoxLayout* m_Layout;
|
|
QLabel* m_Label;
|
|
// Stores the uLib signal connection so it can be disconnected on destruction,
|
|
// preventing use-after-free when PropertyEditor::clear() deletes widgets.
|
|
Connection m_Connection;
|
|
};
|
|
|
|
class UnitLineEdit : public QLineEdit {
|
|
Q_OBJECT
|
|
public:
|
|
UnitLineEdit(QWidget* parent = nullptr);
|
|
void setValue(double val);
|
|
void setUnits(const QString& suffix, double factor = 1.0);
|
|
double getValue() const { return m_Value; }
|
|
void setIntegerOnly(bool b);
|
|
|
|
signals:
|
|
void valueManualChanged(double val);
|
|
|
|
private slots:
|
|
void onEditingFinished();
|
|
|
|
private:
|
|
void updateText();
|
|
double m_Value;
|
|
double m_Factor;
|
|
QString m_Suffix;
|
|
bool m_IsInteger;
|
|
};
|
|
|
|
class DoublePropertyWidget : public PropertyWidgetBase {
|
|
Q_OBJECT
|
|
public:
|
|
DoublePropertyWidget(Property<double>* prop, QWidget* parent = nullptr);
|
|
private:
|
|
Property<double>* m_Prop;
|
|
UnitLineEdit* m_Edit;
|
|
};
|
|
|
|
class FloatPropertyWidget : public PropertyWidgetBase {
|
|
Q_OBJECT
|
|
public:
|
|
FloatPropertyWidget(Property<float>* prop, QWidget* parent = nullptr);
|
|
private:
|
|
Property<float>* m_Prop;
|
|
UnitLineEdit* m_Edit;
|
|
};
|
|
|
|
class IntPropertyWidget : public PropertyWidgetBase {
|
|
Q_OBJECT
|
|
public:
|
|
IntPropertyWidget(Property<int>* prop, QWidget* parent = nullptr);
|
|
private:
|
|
Property<int>* m_Prop;
|
|
UnitLineEdit* m_Edit;
|
|
};
|
|
|
|
template <typename VecT, int Size>
|
|
class VectorPropertyWidget : public PropertyWidgetBase {
|
|
public:
|
|
VectorPropertyWidget(Property<VecT>* prop, QWidget* parent = nullptr)
|
|
: PropertyWidgetBase(prop, parent), m_Prop(prop) {
|
|
|
|
std::string unit = prop->GetUnits();
|
|
double factor = 1.0;
|
|
QString prefSuffix;
|
|
if (!unit.empty()) {
|
|
auto dim = Settings::Instance().IdentifyDimension(unit);
|
|
std::string pref = Settings::Instance().GetPreferredUnit(dim);
|
|
factor = Settings::Instance().GetUnitFactor(pref);
|
|
prefSuffix = QString::fromStdString(pref);
|
|
}
|
|
|
|
for (int i = 0; i < Size; ++i) {
|
|
m_Edits[i] = new UnitLineEdit(this);
|
|
if (std::is_integral<typename VecT::Scalar>::value) {
|
|
m_Edits[i]->setIntegerOnly(true);
|
|
}
|
|
if (!prefSuffix.isEmpty()) {
|
|
m_Edits[i]->setUnits(prefSuffix, factor);
|
|
}
|
|
m_Edits[i]->setEnabled(!prop->IsReadOnly());
|
|
m_Layout->addWidget(m_Edits[i], 1);
|
|
|
|
connect(m_Edits[i], &UnitLineEdit::valueManualChanged, [this, i](double val){
|
|
VecT v = m_Prop->Get();
|
|
v(i) = (typename VecT::Scalar)val;
|
|
if (m_Prop->Get() != v) {
|
|
m_Prop->Set(v);
|
|
emit updated();
|
|
}
|
|
});
|
|
}
|
|
updateEdits();
|
|
m_Connection = uLib::Object::connect(m_Prop, &Property<VecT>::Updated, [this](){
|
|
updateEdits();
|
|
});
|
|
}
|
|
~VectorPropertyWidget() { m_Connection.disconnect(); }
|
|
|
|
private:
|
|
void updateEdits() {
|
|
VecT v = m_Prop->Get();
|
|
for (int i = 0; i < Size; ++i) {
|
|
if (!m_Edits[i]->hasFocus()) {
|
|
m_Edits[i]->setValue((double)v(i));
|
|
}
|
|
}
|
|
}
|
|
Property<VecT>* m_Prop;
|
|
UnitLineEdit* m_Edits[Size];
|
|
};
|
|
|
|
class RangePropertyWidget : public PropertyWidgetBase {
|
|
Q_OBJECT
|
|
public:
|
|
RangePropertyWidget(Property<double>* prop, QWidget* parent = nullptr);
|
|
virtual ~RangePropertyWidget();
|
|
private slots:
|
|
void onSliderChanged(int val);
|
|
private:
|
|
void updateUi();
|
|
Property<double>* m_Prop;
|
|
QSlider* m_Slider;
|
|
UnitLineEdit* m_Edit;
|
|
};
|
|
|
|
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 ColorPropertyWidget : public PropertyWidgetBase {
|
|
Q_OBJECT
|
|
public:
|
|
ColorPropertyWidget(Property<Vector3d>* prop, QWidget* parent = nullptr);
|
|
virtual ~ColorPropertyWidget();
|
|
private slots:
|
|
void onClicked();
|
|
private:
|
|
void updateButtonColor();
|
|
Property<Vector3d>* m_Prop;
|
|
QPushButton* m_Button;
|
|
};
|
|
|
|
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 FontPropertyWidget : public PropertyWidgetBase {
|
|
Q_OBJECT
|
|
public:
|
|
FontPropertyWidget(Property<FontConfig>* prop, QWidget* parent = nullptr);
|
|
virtual ~FontPropertyWidget();
|
|
private slots:
|
|
void onClicked();
|
|
private:
|
|
void updateButtonText();
|
|
Property<FontConfig>* m_Prop;
|
|
QPushButton* m_Button;
|
|
};
|
|
|
|
class ReferencePropertyWidget : public PropertyWidgetBase {
|
|
Q_OBJECT
|
|
public:
|
|
ReferencePropertyWidget(ReferencePropertyBase* prop, ::uLib::ObjectsContext* context, QWidget* parent = nullptr);
|
|
virtual ~ReferencePropertyWidget();
|
|
private slots:
|
|
void onComboChanged(int index);
|
|
private:
|
|
void refreshCombo();
|
|
ReferencePropertyBase* m_RefProp;
|
|
::uLib::ObjectsContext* m_Context;
|
|
QComboBox* m_Combo;
|
|
Connection m_ContextConnection;
|
|
};
|
|
|
|
class PropertyEditor : public QWidget {
|
|
Q_OBJECT
|
|
public:
|
|
PropertyEditor(QWidget* parent = nullptr);
|
|
virtual ~PropertyEditor();
|
|
void setObject(uLib::Object* obj, bool displayOnly = false);
|
|
void setContext(uLib::ObjectsContext* context) { m_Context = context; }
|
|
template<typename T>
|
|
void registerFactory(std::function<QWidget*(PropertyBase*, QWidget*)> factory) {
|
|
m_Factories[std::type_index(typeid(T))] = factory;
|
|
}
|
|
|
|
signals:
|
|
void propertyUpdated(PropertyBase* prop = nullptr);
|
|
|
|
private:
|
|
void clear();
|
|
uLib::Object* m_Object;
|
|
uLib::ObjectsContext* m_Context;
|
|
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
|