Handle properties in a generic manner in the object editor

Sun, 13 Mar 2022 18:46:10 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Sun, 13 Mar 2022 18:46:10 +0200
changeset 180
5b7a8f2270ff
parent 179
7b9b85b459de
child 181
79de20dc6a1e

Handle properties in a generic manner in the object editor

CMakeLists.txt file | annotate | diff | comparison | revisions
src/ui/objecteditor.cpp file | annotate | diff | comparison | revisions
src/ui/objecteditor.h file | annotate | diff | comparison | revisions
src/ui/polygonobjecteditor.cpp file | annotate | diff | comparison | revisions
src/ui/polygonobjecteditor.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Sun Mar 13 14:53:14 2022 +0200
+++ b/CMakeLists.txt	Sun Mar 13 18:46:10 2022 +0200
@@ -69,7 +69,6 @@
 	src/ui/canvas.cpp
 	src/ui/multiplyfactordialog.cpp
 	src/ui/objecteditor.cpp
-	src/ui/polygonobjecteditor.cpp
 	src/widgets/colorbutton.cpp
 	src/widgets/colorindexinput.cpp
 	src/widgets/colorselectdialog.cpp
@@ -129,7 +128,6 @@
 	src/ui/canvas.h
 	src/ui/multiplyfactordialog.h
 	src/ui/objecteditor.h
-	src/ui/polygonobjecteditor.h
 	src/widgets/colorbutton.h
 	src/widgets/colorindexinput.h
 	src/widgets/colorselectdialog.h
--- a/src/ui/objecteditor.cpp	Sun Mar 13 14:53:14 2022 +0200
+++ b/src/ui/objecteditor.cpp	Sun Mar 13 18:46:10 2022 +0200
@@ -22,7 +22,6 @@
 	objectTypeNameLabel{new QLabel{this}},
 	objectTypeIconLabel{new QLabel{this}}
 {
-	this->polygonEditor.emplace(this->document, id);
 	this->setLayout(new QVBoxLayout{this});
 	QWidget* objectTitleLayoutContainer = new QWidget{this};
 	QLayout* objectTitleLayout = new QHBoxLayout{objectTitleLayoutContainer};
@@ -31,31 +30,26 @@
 	objectTitleLayout->addWidget(this->objectTypeNameLabel);
 	objectTitleLayout->addWidget(new QSplitter{Qt::Horizontal, this});
 	this->layout()->addWidget(objectTitleLayoutContainer);
-	this->layout()->addWidget(&*this->polygonEditor);
 	this->layout()->addWidget(formContainer);
 	this->setObjectId(id);
-	
+
 	QWidget* const parent = this->formContainer;
 	QFormLayout* formLayout = new QFormLayout{parent};
 	this->formContainer->setLayout(formLayout);
-	QLabel* colorLabel = new QLabel{"Color", parent};
-	ColorIndexInput* colorWidget = new ColorIndexInput{document, {0}, parent};
-	formLayout->addRow(colorLabel, colorWidget);
-	connect(colorWidget, &ColorIndexInput::colorChanged, [this](ldraw::Color color)
+
+	for (const ldraw::Property property : ldraw::ALL_PROPERTIES)
 	{
-		const QModelIndex index = this->document->getModel().find(this->objectId);
-		if (index.isValid())
+		QWidget* editorWidget = this->makeEditorWidgetForProperty(property);
+		if (editorWidget != nullptr)
 		{
-			this->document->editModel()->modifyObjectAt<ldraw::ColoredObject>(
-				index.row(),
-				[color](ldraw::ColoredObject* object)
-				{
-					object->colorIndex = color;
-				}
-			);
+			editorWidget->setProperty("_property_id", static_cast<int>(property));
+			QLabel* propertyLabel = new QLabel{ldraw::traits(property).name.data()};
+			formLayout->addRow(propertyLabel, editorWidget);
+			this->propertyWidgets[property] = {propertyLabel, editorWidget};
 		}
-	});
-	this->propertyWidgets[ldraw::Property::Color] = {colorLabel, colorWidget};
+	}
+
+	this->setObjectId(ldraw::NULL_ID);
 }
 
 QString titleCase(const QString& string)
@@ -63,34 +57,50 @@
 	return string.left(1).toUpper() + string.mid(1);
 }
 
+void setValueToWidget(QWidget* widget, const QVariant& value)
+{
+	ColorIndexInput* colorIndexInput = qobject_cast<ColorIndexInput*>(widget);
+	if (colorIndexInput != nullptr)
+	{
+		colorIndexInput->setSelectedColor(value.value<ldraw::Color>());
+	}
+	else
+	{
+		Vec3Editor* vec3Editor = qobject_cast<Vec3Editor*>(widget);
+		if (vec3Editor != nullptr)
+		{
+			vec3Editor->setValue(value.value<glm::vec3>());
+		}
+	}
+}
+
 void ObjectEditor::setObjectId(const ldraw::id_t id)
 {
 	this->objectId = id;
 	const ldraw::Object* object = this->document->getModel().get(id);
-	this->clear();
 	if (object != nullptr)
 	{
 		this->objectTypeNameLabel->setText("<b>" + titleCase(object->typeName()) + "</b>");
 		this->objectTypeIconLabel->setPixmap(QPixmap{object->iconName()}.scaledToWidth(24));
-		if (object->numPoints() > 0)
-		{
-			this->polygonEditor->setObjectId(id);
-		}
-		else
+		for (const ldraw::Property property : ldraw::ALL_PROPERTIES)
 		{
-			this->polygonEditor->clear();
-		}
-		constexpr ldraw::Property prop = ldraw::Property::Color;
-		QVariant color = object->getProperty(prop);
-		this->propertyWidgets[prop].first->setVisible(not color.isNull());
-		this->propertyWidgets[prop].second->setVisible(not color.isNull());
-		if (not color.isNull())
-		{
-			static_cast<ColorIndexInput*>(this->propertyWidgets[prop].second)->setSelectedColor(color.value<ldraw::Color>());
+			const QVariant value = object->getProperty(property);
+			const auto it = this->propertyWidgets.find(property);
+			if (it != this->propertyWidgets.end())
+			{
+				it->first->setVisible(not value.isNull());
+				it->second->setVisible(not value.isNull());
+				if (not value.isNull())
+				{
+					setValueToWidget(it->second, value);
+				}
+			}
 		}
 	}
 	else
 	{
+		this->objectTypeNameLabel->clear();
+		this->objectTypeIconLabel->clear();
 		for (auto& pair : this->propertyWidgets)
 		{
 			pair.first->setVisible(false);
@@ -99,10 +109,43 @@
 	}
 }
 
-void ObjectEditor::clear()
+void ObjectEditor::handleColorChange(ldraw::Color value)
+{
+	this->handlePropertyChange(this->sender(), QVariant::fromValue(value));
+}
+
+void ObjectEditor::handleVec3Change(const glm::vec3 &value)
+{
+	this->handlePropertyChange(this->sender(), QVariant::fromValue(value));
+}
+
+QWidget* ObjectEditor::makeEditorWidgetForProperty(ldraw::Property property)
 {
-	this->objectTypeNameLabel->clear();
-	this->objectTypeIconLabel->clear();
-	this->polygonEditor->clear();
-	delete this->formContainer->layout();
+	QWidget* const parent = qobject_cast<QWidget*>(this->parent());
+	if (ldraw::traits(property).type == qMetaTypeId<ldraw::Color>())
+	{
+		ColorIndexInput* colorWidget = new ColorIndexInput{this->document, {0}, parent};
+		connect(colorWidget, &ColorIndexInput::colorChanged, this, &ObjectEditor::handleColorChange);
+		return colorWidget;
+	}
+	else if (ldraw::traits(property).type == qMetaTypeId<glm::vec3>())
+	{
+		Vec3Editor* editor = new Vec3Editor{{}, parent};
+		connect(editor, &Vec3Editor::valueChanged, this, &ObjectEditor::handleVec3Change);
+		return editor;
+	}
+	else
+	{
+		return nullptr;
+	}
 }
+
+void ObjectEditor::handlePropertyChange(QObject *caller, const QVariant &value)
+{
+	QVariant propertyVariant = caller->property("_property_id");
+	if (not propertyVariant.isNull())
+	{
+		ldraw::Property const property = static_cast<ldraw::Property>(propertyVariant.toInt());
+		this->document->editModel()->setObjectProperty(this->objectId, property, value);
+	}
+}
\ No newline at end of file
--- a/src/ui/objecteditor.h	Sun Mar 13 14:53:14 2022 +0200
+++ b/src/ui/objecteditor.h	Sun Mar 13 18:46:10 2022 +0200
@@ -12,13 +12,15 @@
 public:
 	explicit ObjectEditor(Document* document, ldraw::id_t id = ldraw::NULL_ID);
 	void setObjectId(ldraw::id_t id);
-	void clear();
 private:
+	Q_SLOT void handleColorChange(ldraw::Color value);
+	Q_SLOT void handleVec3Change(const glm::vec3& value);
 	Document* const document;
 	ldraw::id_t objectId = ldraw::NULL_ID;
-	std::optional<PolygonObjectEditor> polygonEditor;
 	QWidget* formContainer;
 	class QLabel* objectTypeNameLabel;
 	class QLabel* objectTypeIconLabel;
 	QMap<ldraw::Property, QPair<QWidget*, QWidget*>> propertyWidgets;
+	QWidget* makeEditorWidgetForProperty(ldraw::Property property);
+	void handlePropertyChange(QObject* caller, const QVariant& value);
 };
--- a/src/ui/polygonobjecteditor.cpp	Sun Mar 13 14:53:14 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-#include <QFormLayout>
-#include <QSplitter>
-#include "document.h"
-#include "modeleditor.h"
-#include "widgets/vec3editor.h"
-#include "ui/polygonobjecteditor.h"
-
-static constexpr char INDEX_NAME[] = "_ldforge_index";
-static constexpr char LABEL_NAME[] = "_ldforge_label";
-
-PolygonObjectEditor::PolygonObjectEditor(Document* document, ldraw::id_t id) :
-	QWidget{document},
-	document{document},
-	storedObjectId{ldraw::NULL_ID.value}
-{
-	this->splitter.emplace(Qt::Vertical, this);
-	this->buildWidgets();
-	this->setObjectId(id);
-}
-
-// destructor needed for std::unique_ptr
-PolygonObjectEditor::~PolygonObjectEditor()
-{
-}
-
-ldraw::id_t PolygonObjectEditor::objectId() const
-{
-	return this->storedObjectId;
-}
-
-void PolygonObjectEditor::setObjectId(ldraw::id_t id)
-{
-	this->storedObjectId = id;
-	const QModelIndex index = this->document->getModel().find(this->objectId());
-	if (index.isValid())
-	{
-		for (int n : {0, 1, 2, 3})
-		{
-			const ldraw::Object* const object = this->document->getModel()[index.row()];
-			const ldraw::Property property = ldraw::pointProperty(n);
-			const QVariant value = object->getProperty(property);
-			this->widgets[n]->setVisible(value.isValid());
-			this->formLayout->itemAt(n, QFormLayout::LabelRole)->widget()->setVisible(value.isValid());
-			if (value.isValid())
-			{
-				this->widgets[n]->setValue(value.value<glm::vec3>());
-			}
-		}
-	}
-	else
-	{
-		for (int n : {0, 1, 2, 3})
-		{
-			this->widgets[n]->setVisible(false);
-			this->formLayout->itemAt(n, QFormLayout::LabelRole)->widget()->setVisible(false);
-		}
-	}
-}
-
-void PolygonObjectEditor::clear()
-{
-	this->setObjectId(ldraw::NULL_ID);
-}
-
-void PolygonObjectEditor::buildWidgets()
-{
-	this->formLayout = new QFormLayout{this};
-	this->setLayout(this->formLayout);
-	for (int n : {0, 1, 2, 3})
-	{
-		this->setupPointWidget(n);
-	}
-	for (std::unique_ptr<Vec3Editor>& widget : this->widgets)
-	{
-		const QString label = widget->property(LABEL_NAME).toString();
-		this->formLayout->addRow(label, widget.get());
-	}
-	this->formLayout->addRow("", &*this->splitter);
-}
-
-void PolygonObjectEditor::setupPointWidget(int n)
-{
-	std::unique_ptr<Vec3Editor> editor = std::make_unique<Vec3Editor>(glm::vec3{}, this);
-	QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged);
-	editor->setProperty(INDEX_NAME, QVariant::fromValue(n));
-	editor->setProperty(LABEL_NAME, &ldraw::traits(ldraw::pointProperty(n)).name[0]);
-	this->widgets.push_back(std::move(editor));
-}
-
-void PolygonObjectEditor::pointChanged(const glm::vec3& value)
-{
-	std::unique_ptr<ModelEditor> modelEditor = this->document->editModel();
-	const int n = this->sender()->property(INDEX_NAME).toInt();
-	const ldraw::Property property = ldraw::pointProperty(n);
-	modelEditor->setObjectProperty(this->objectId(), property, QVariant::fromValue(value));
-}
--- a/src/ui/polygonobjecteditor.h	Sun Mar 13 14:53:14 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-#pragma once
-#include <QWidget>
-#include <QSplitter>
-#include "main.h"
-#include "../widgets/vec3editor.h"
-
-class Document;
-
-class PolygonObjectEditor : public QWidget
-{
-public:
-	PolygonObjectEditor(Document* document, ldraw::id_t id);
-	~PolygonObjectEditor();
-	ldraw::id_t objectId() const;
-	void setObjectId(ldraw::id_t id);
-	void clear();
-private:
-	void buildWidgets();
-	void setupPointWidget(int n);
-	Q_SLOT void pointChanged(const glm::vec3& value);
-	Document* document;
-	ldraw::id_t storedObjectId;
-	std::vector<std::unique_ptr<Vec3Editor>> widgets;
-	std::optional<QSplitter> splitter;
-	class QFormLayout* formLayout = nullptr;
-};

mercurial