Move some widgets into a static library

Wed, 22 Jun 2022 20:27:53 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Wed, 22 Jun 2022 20:27:53 +0300
changeset 252
da4876bfd822
parent 251
94b0a30a1886
child 253
8b994c917f69

Move some widgets into a static library

CMakeLists.txt file | annotate | diff | comparison | revisions
src/ui/multiplyfactordialog.cpp file | annotate | diff | comparison | revisions
src/ui/multiplyfactordialog.h file | annotate | diff | comparison | revisions
src/ui/multiplyfactordialog.ui file | annotate | diff | comparison | revisions
src/ui/objecteditor.cpp file | annotate | diff | comparison | revisions
src/widgets/matrixeditor.cpp file | annotate | diff | comparison | revisions
src/widgets/matrixeditor.h file | annotate | diff | comparison | revisions
src/widgets/matrixeditor.ui file | annotate | diff | comparison | revisions
src/widgets/vec3editor.cpp file | annotate | diff | comparison | revisions
src/widgets/vec3editor.h file | annotate | diff | comparison | revisions
src/widgets/vec3editor.ui file | annotate | diff | comparison | revisions
widgets/matrixeditor.cpp file | annotate | diff | comparison | revisions
widgets/matrixeditor.h file | annotate | diff | comparison | revisions
widgets/matrixeditor.ui file | annotate | diff | comparison | revisions
widgets/multiplyfactordialog.cpp file | annotate | diff | comparison | revisions
widgets/multiplyfactordialog.h file | annotate | diff | comparison | revisions
widgets/multiplyfactordialog.ui file | annotate | diff | comparison | revisions
widgets/vec3editor.cpp file | annotate | diff | comparison | revisions
widgets/vec3editor.h file | annotate | diff | comparison | revisions
widgets/vec3editor.ui file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Wed Jun 22 20:10:57 2022 +0300
+++ b/CMakeLists.txt	Wed Jun 22 20:27:53 2022 +0300
@@ -21,6 +21,22 @@
 add_definitions(-DQT_NO_KEYWORDS)
 source_group("LDForge" REGULAR_EXPRESSION "src/.+\\.(cpp|h|ui)")
 
+qt5_wrap_ui(LDFORGEWIDGETS_FORMS
+	widgets/vec3editor.ui
+	widgets/multiplyfactordialog.ui
+	widgets/matrixeditor.ui
+)
+add_library(ldforgewidgets STATIC
+	widgets/matrixeditor.cpp
+	widgets/matrixeditor.h
+	widgets/vec3editor.cpp
+	widgets/vec3editor.h
+	widgets/multiplyfactordialog.cpp
+	widgets/multiplyfactordialog.h
+	${LDFORGEWIDGETS_FORMS}
+)
+target_link_libraries(ldforgewidgets Qt5::Widgets)
+
 set (LDFORGE_SOURCES
 	src/colors.cpp
 	src/document.cpp
@@ -49,14 +65,11 @@
 	src/settingseditor/settingseditor.cpp
 	src/types/boundingbox.cpp
 	src/ui/circletooloptionswidget.cpp
-	src/ui/multiplyfactordialog.cpp
 	src/ui/objecteditor.cpp
 	src/widgets/colorbutton.cpp
 	src/widgets/colorindexinput.cpp
 	src/widgets/colorselectdialog.cpp
 	src/widgets/doublespinbox.cpp
-	src/widgets/matrixeditor.cpp
-	src/widgets/vec3editor.cpp
 )
 set (LDFORGE_HEADERS
 	src/basics.h
@@ -93,26 +106,20 @@
 	src/settingseditor/settingseditor.h
 	src/types/boundingbox.h
 	src/ui/circletooloptionswidget.h
-	src/ui/multiplyfactordialog.h
 	src/ui/objecteditor.h
 	src/widgets/colorbutton.h
 	src/widgets/colorindexinput.h
 	src/widgets/colorselectdialog.h
 	src/widgets/doublespinbox.h
-	src/widgets/matrixeditor.h
-	src/widgets/vec3editor.h
 )
 set (LDFORGE_FORMS
 	src/mainwindow.ui
 	src/settingseditor/librarieseditor.ui
 	src/settingseditor/settingseditor.ui
 	src/ui/circletool.ui
-	src/ui/multiplyfactordialog.ui
 	src/ui/objecteditor.ui
 	src/widgets/colorselectdialog.ui
 	src/widgets/colorindexinput.ui
-	src/widgets/matrixeditor.ui
-	src/widgets/vec3editor.ui
 )
 
 set(LDFORGE_LOCALES
@@ -130,6 +137,7 @@
 set(CMAKE_CXX_EXTENSIONS OFF)
 include_directories("${PROJECT_BINARY_DIR}")
 include_directories("${PROJECT_BINARY_DIR}/src")
+include_directories("${PROJECT_SOURCE_DIR}")
 include_directories("${PROJECT_SOURCE_DIR}/src")
 
 # Translations
@@ -184,6 +192,7 @@
 set_source_files_properties(${LDFORGE_OTHER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)
 set_target_properties(ldforge PROPERTIES AUTOMOC 1)
 target_link_libraries(ldforge Qt5::Widgets Qt5::Network Qt5::OpenGL ${OPENGL_LIBRARIES})
+target_link_libraries(ldforge ldforgewidgets)
 add_dependencies(ldforge resources)
 #cotire(ldforge)
 
--- a/src/ui/multiplyfactordialog.cpp	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-#include "multiplyfactordialog.h"
-#include "ui_multiplyfactordialog.h"
-
-MultiplyFactorDialog::MultiplyFactorDialog(const glm::vec3& baseVector, QWidget* parent) :
-	QDialog{parent},
-	baseVector{baseVector},
-	preview{baseVector, parent, Vec3Editor::NoMultiplyButton}
-{
-	ui = std::make_unique<Ui::MultiplyFactorDialog>();
-	ui->setupUi(this);
-	this->preview.setEnabled(false);
-	this->ui->previewGroupBox->setLayout(new QVBoxLayout{parent});
-	this->ui->previewGroupBox->layout()->addWidget(&this->preview);
-	connect(
-		this->ui->invert,
-		&QCheckBox::clicked,
-		this,
-		&MultiplyFactorDialog::updatePreview);
-	connect(
-		this->ui->factor,
-		qOverload<double>(&DoubleSpinBox::valueChanged),
-		this,
-		&MultiplyFactorDialog::updatePreview);
-}
-
-/**
- * @brief empty destructor, necessary because std::unique_ptr is used with a forward declaration
- */
-MultiplyFactorDialog::~MultiplyFactorDialog()
-{
-}
-
-/**
- * @brief Computes the resulting vector
- * @return the input vector multiplied by the specified vector
- */
-glm::vec3 MultiplyFactorDialog::value() const
-{
-	glm::vec3 result = baseVector;
-	if (this->ui->invert->isChecked())
-	{
-		if (qFuzzyIsNull(this->ui->factor->value()))
-		{
-			constexpr double infinity = std::numeric_limits<double>::quiet_NaN();
-			result = {infinity, infinity, infinity};
-		}
-		else
-		{
-			result /= this->ui->factor->value();
-		}
-	}
-	else
-	{
-		result *= this->ui->factor->value();
-	}
-	return result;
-}
-
-/**
- * @brief Makes a string that is prefixed to the factor input.
- * @param ui
- * @return prefix string
- */
-QString prefixForFactorInput(const Ui::MultiplyFactorDialog& ui)
-{
-	if (ui.invert->isChecked())
-	{
-		return "1 : ";
-	}
-	else
-	{
-		return "";
-	}
-}
-
-/**
- * @brief Makes a string that is suffixed to the factor input.
- * @param ui
- * @return prefix string
- */
-QString suffixForFactorInput(const Ui::MultiplyFactorDialog& ui)
-{
-	if (ui.invert->isChecked())
-	{
-		// render the actual factor that stuff gets effectively multiplied by
-		return " = " + QString::number(1.0 / (ui.factor->value()));
-	}
-	else
-	{
-		return "";
-	}
-}
-
-/**
- * @brief Responds to changes in the value and updates previews accordingly
- */
-void MultiplyFactorDialog::updatePreview()
-{
-	this->ui->factor->setPrefix(::prefixForFactorInput(*this->ui));
-	this->ui->factor->setSuffix(::suffixForFactorInput(*this->ui));
-	this->preview.setValue(this->value());
-}
--- a/src/ui/multiplyfactordialog.h	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-#pragma once
-#include <QDialog>
-#include "../basics.h"
-#include "../widgets/vec3editor.h"
-
-namespace Ui
-{
-	class MultiplyFactorDialog;
-}
-
-class MultiplyFactorDialog : public QDialog
-{
-	Q_OBJECT
-public:
-	explicit MultiplyFactorDialog(const glm::vec3& baseVector = glm::vec3{}, QWidget *parent = nullptr);
-	~MultiplyFactorDialog();
-	glm::vec3 value() const;
-private:
-	Q_SLOT void updatePreview();
-	std::unique_ptr<Ui::MultiplyFactorDialog> ui;
-	const glm::vec3 baseVector;
-	Vec3Editor preview;
-};
--- a/src/ui/multiplyfactordialog.ui	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MultiplyFactorDialog</class>
- <widget class="QDialog" name="MultiplyFactorDialog">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>286</width>
-    <height>169</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Multiply with a scalar</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
-   <item>
-    <layout class="QFormLayout" name="formLayout">
-     <item row="0" column="0">
-      <widget class="QLabel" name="label">
-       <property name="text">
-        <string>Factor:</string>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="1">
-      <widget class="DoubleSpinBox" name="factor">
-       <property name="value">
-        <double>1.000000000000000</double>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <widget class="QCheckBox" name="invert">
-       <property name="text">
-        <string>Invert</string>
-       </property>
-      </widget>
-     </item>
-    </layout>
-   </item>
-   <item>
-    <widget class="QGroupBox" name="previewGroupBox">
-     <property name="title">
-      <string>Preview</string>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QDialogButtonBox" name="buttonBox">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
-     </property>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>DoubleSpinBox</class>
-   <extends>QDoubleSpinBox</extends>
-   <header>widgets/doublespinbox.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>accepted()</signal>
-   <receiver>MultiplyFactorDialog</receiver>
-   <slot>accept()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>248</x>
-     <y>254</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>157</x>
-     <y>274</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>rejected()</signal>
-   <receiver>MultiplyFactorDialog</receiver>
-   <slot>reject()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>316</x>
-     <y>260</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>286</x>
-     <y>274</y>
-    </hint>
-   </hints>
-  </connection>
- </connections>
-</ui>
--- a/src/ui/objecteditor.cpp	Wed Jun 22 20:10:57 2022 +0300
+++ b/src/ui/objecteditor.cpp	Wed Jun 22 20:27:53 2022 +0300
@@ -1,11 +1,11 @@
 #include <QCheckBox>
 #include <QLineEdit>
 #include <QFormLayout>
+#include <widgets/vec3editor.h>
 #include "objecteditor.h"
 #include "document.h"
 #include "widgets/colorbutton.h"
 #include "widgets/colorindexinput.h"
-#include "widgets/vec3editor.h"
 #include "ui_objecteditor.h"
 
 using PropertyPointer = std::variant<
--- a/src/widgets/matrixeditor.cpp	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#include "basics.h"
-#include "matrixeditor.h"
-#include "ui_matrixeditor.h"
-#include "../ui/multiplyfactordialog.h"
-
-constexpr char BUTTON_COLUMN_PROPERTY[] = "_ldforge_column";
-
-MatrixEditor::MatrixEditor(const glm::mat4 value, QWidget* parent) :
-	QWidget(parent),
-	ui(new Ui::MatrixEditor)
-{
-	ui->setupUi(this);
-	for (int column = 0; column < countof(this->spinboxes); column += 1)
-	{
-		for (int row = 0; row < countof(this->spinboxes[0]); row += 1)
-		{
-			const QString name = QStringLiteral("cell%1%2").arg(column).arg(row);
-			QDoubleSpinBox** spinbox = &this->spinboxes[column][row];
-			*spinbox = this->findChild<QDoubleSpinBox*>(name);
-			connect(*spinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), [&]()
-			{
-				Q_EMIT this->valueChanged(this->value());
-			});
-			Q_ASSERT(*spinbox != nullptr);
-		}
-		const QString multiplyButtonName = QStringLiteral("multiply%1").arg(column);
-		QAbstractButton* button = this->findChild<QAbstractButton*>(multiplyButtonName);
-		button->setProperty(BUTTON_COLUMN_PROPERTY, column);
-		connect(button, &QAbstractButton::clicked, this, &MatrixEditor::multiplyButtonPressed);
-	}
-	this->setValue(value);
-}
-
-MatrixEditor::MatrixEditor(QWidget *parent) :
-	MatrixEditor{glm::mat4{1}, parent}
-{
-}
-
-MatrixEditor::~MatrixEditor()
-{
-	delete ui;
-}
-
-glm::mat4 MatrixEditor::value() const
-{
-	glm::mat4 result{1};
-	for (int column = 0; column < countof(this->spinboxes); column += 1)
-	{
-		for (int row = 0; row < countof(this->spinboxes[0]); row += 1)
-		{
-			result[column][row] = narrow<float>(this->spinboxes[column][row]->value());
-		}
-	}
-	return result;
-}
-
-void MatrixEditor::setValue(const glm::mat4& value)
-{
-	for (int column = 0; column < countof(this->spinboxes); column += 1)
-	{
-		for (int row = 0; row < countof(this->spinboxes[0]); row += 1)
-		{
-			QDoubleSpinBox* spinbox = this->spinboxes[column][row];
-			QSignalBlocker blocker{spinbox};
-			spinbox->setValue(value[column][row]);
-		}
-	}
-}
-
-void MatrixEditor::multiplyButtonPressed()
-{
-	QAbstractButton* button = qobject_cast<QAbstractButton*>(this->sender());
-	if (button != nullptr)
-	{
-		bool ok;
-		const int column = button->property(BUTTON_COLUMN_PROPERTY).toInt(&ok);
-		if (ok and column >= 0 and column < this->matrixSize())
-		{
-			glm::mat4 newValue = this->value();
-			MultiplyFactorDialog dialog{newValue[column], this};
-			const int result = dialog.exec();
-			if (result == QDialog::Accepted)
-			{
-				newValue[column] = glm::vec4{dialog.value(), (column == 3) ? 1 : 0};
-				this->setValue(newValue);
-				Q_EMIT valueChanged(newValue);
-			}
-		}
-	}
-}
--- a/src/widgets/matrixeditor.h	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#pragma once
-#include <QWidget>
-#include "basics.h"
-
-namespace Ui {
-class MatrixEditor;
-}
-
-class MatrixEditor : public QWidget
-{
-	Q_OBJECT
-public:
-	explicit MatrixEditor(QWidget *parent = nullptr);
-	explicit MatrixEditor(const glm::mat4 value, QWidget* parent = nullptr);
-	~MatrixEditor();
-	glm::mat4 value() const;
-	void setValue(const glm::mat4& value);
-Q_SIGNALS:
-	void valueChanged(const glm::mat4& value);
-private:
-	constexpr int matrixSize() const;
-	Q_SLOT void multiplyButtonPressed();
-	class QDoubleSpinBox* spinboxes[4][3];
-	Ui::MatrixEditor *ui;
-};
-
-constexpr int MatrixEditor::matrixSize() const
-{
-	return 4;
-}
--- a/src/widgets/matrixeditor.ui	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MatrixEditor</class>
- <widget class="QWidget" name="MatrixEditor">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>356</width>
-    <height>172</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <layout class="QGridLayout" name="gridLayout">
-   <item row="0" column="0">
-    <widget class="DoubleSpinBox" name="cell00"/>
-   </item>
-   <item row="0" column="1">
-    <widget class="DoubleSpinBox" name="cell10"/>
-   </item>
-   <item row="0" column="2">
-    <widget class="DoubleSpinBox" name="cell20"/>
-   </item>
-   <item row="0" column="3">
-    <widget class="DoubleSpinBox" name="cell30"/>
-   </item>
-   <item row="1" column="0">
-    <widget class="DoubleSpinBox" name="cell01"/>
-   </item>
-   <item row="1" column="1">
-    <widget class="DoubleSpinBox" name="cell11"/>
-   </item>
-   <item row="1" column="2">
-    <widget class="DoubleSpinBox" name="cell21"/>
-   </item>
-   <item row="1" column="3">
-    <widget class="DoubleSpinBox" name="cell31"/>
-   </item>
-   <item row="2" column="0">
-    <widget class="DoubleSpinBox" name="cell02"/>
-   </item>
-   <item row="2" column="1">
-    <widget class="DoubleSpinBox" name="cell12"/>
-   </item>
-   <item row="2" column="2">
-    <widget class="DoubleSpinBox" name="cell22"/>
-   </item>
-   <item row="2" column="3">
-    <widget class="DoubleSpinBox" name="cell32"/>
-   </item>
-   <item row="3" column="0">
-    <widget class="DoubleSpinBox" name="doubleSpinBox">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-    </widget>
-   </item>
-   <item row="3" column="1">
-    <widget class="DoubleSpinBox" name="doubleSpinBox_2">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-    </widget>
-   </item>
-   <item row="3" column="2">
-    <widget class="DoubleSpinBox" name="doubleSpinBox_3">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-    </widget>
-   </item>
-   <item row="3" column="3">
-    <widget class="DoubleSpinBox" name="doubleSpinBox_4">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-     <property name="value">
-      <double>1.000000000000000</double>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="0">
-    <widget class="QPushButton" name="multiply0">
-     <property name="text">
-      <string>×</string>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="1">
-    <widget class="QPushButton" name="multiply1">
-     <property name="text">
-      <string>×</string>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="2">
-    <widget class="QPushButton" name="multiply2">
-     <property name="text">
-      <string>×</string>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="3">
-    <widget class="QPushButton" name="multiply3">
-     <property name="text">
-      <string>×</string>
-     </property>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>DoubleSpinBox</class>
-   <extends>QDoubleSpinBox</extends>
-   <header>widgets/doublespinbox.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
--- a/src/widgets/vec3editor.cpp	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-#include <QDialog>
-#include <QCheckBox>
-#include <QSignalBlocker>
-#include "vec3editor.h"
-#include "ui_vec3editor.h"
-#include "../ui/multiplyfactordialog.h"
-
-Vec3Editor::Vec3Editor(const glm::vec3& value, QWidget *parent, QFlags<Flag> flags) :
-	QWidget{parent},
-	ui{new Ui::Vec3Editor}
-{
-	this->ui->setupUi(this);
-	this->setValue(value);
-	if (flags.testFlag(NoMultiplyButton))
-	{
-		this->ui->multiply->setVisible(false);
-	}
-	else
-	{
-		connect(this->ui->multiply, &QPushButton::clicked, this, &Vec3Editor::multiplyPressed);
-	}
-	for (QDoubleSpinBox* spinbox : this->spinboxes())
-	{
-		connect(spinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), [&](double)
-		{
-			Q_EMIT this->valueChanged(this->value());
-		});
-	}
-}
-
-Vec3Editor::~Vec3Editor()
-{
-}
-
-glm::vec3 Vec3Editor::value() const
-{
-	auto get = [](DoubleSpinBox* spinbox){ return float_cast(spinbox->value()); };
-	return {get(this->ui->x), get(this->ui->y), get(this->ui->z)};
-}
-
-void Vec3Editor::setValue(const glm::vec3& value)
-{
-	auto set = [](DoubleSpinBox* spinbox, float value)
-	{
-		QSignalBlocker blocker{spinbox};
-		spinbox->setValue(qreal_cast(value));
-	};
-	set(this->ui->x, value.x);
-	set(this->ui->y, value.y);
-	set(this->ui->z, value.z);
-	Q_EMIT this->valueChanged(value);
-}
-
-std::array<DoubleSpinBox*, 3> Vec3Editor::spinboxes()
-{
-	return {this->ui->x, this->ui->y, this->ui->z};
-}
-
-void Vec3Editor::multiplyPressed()
-{
-	MultiplyFactorDialog dialog{this->value(), this};
-	const int dialogResult = dialog.exec();
-	if (dialogResult == QDialog::Accepted)
-	{
-		this->setValue(dialog.value());
-	}
-}
--- a/src/widgets/vec3editor.h	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#pragma once
-#include <QWidget>
-#include "basics.h"
-
-namespace Ui
-{
-	class Vec3Editor;
-}
-
-class Vec3Editor : public QWidget
-{
-	Q_OBJECT
-public:
-	enum Flag
-	{
-		NoMultiplyButton = 0x1
-	};
-	explicit Vec3Editor(const glm::vec3& value, QWidget* parent = nullptr, QFlags<Flag> flags = {});
-	~Vec3Editor();
-	glm::vec3 value() const;
-	void setValue(const glm::vec3& value);
-Q_SIGNALS:
-	void valueChanged(const glm::vec3& value);
-private:
-	std::array<class DoubleSpinBox*, 3> spinboxes();
-	Q_SLOT void multiplyPressed();
-	std::unique_ptr<Ui::Vec3Editor> ui;
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags<Vec3Editor::Flag>)
--- a/src/widgets/vec3editor.ui	Wed Jun 22 20:10:57 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Vec3Editor</class>
- <widget class="QWidget" name="Vec3Editor">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>613</width>
-    <height>46</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1,1,0">
-   <item>
-    <widget class="DoubleSpinBox" name="x">
-     <property name="prefix">
-      <string>x = </string>
-     </property>
-     <property name="decimals">
-      <number>4</number>
-     </property>
-     <property name="minimum">
-      <double>-1000000.000000000000000</double>
-     </property>
-     <property name="maximum">
-      <double>1000000.000000000000000</double>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="DoubleSpinBox" name="y">
-     <property name="prefix">
-      <string>y = </string>
-     </property>
-     <property name="decimals">
-      <number>4</number>
-     </property>
-     <property name="minimum">
-      <double>-1000000.000000000000000</double>
-     </property>
-     <property name="maximum">
-      <double>1000000.000000000000000</double>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="DoubleSpinBox" name="z">
-     <property name="prefix">
-      <string>z = </string>
-     </property>
-     <property name="decimals">
-      <number>4</number>
-     </property>
-     <property name="minimum">
-      <double>-1000000.000000000000000</double>
-     </property>
-     <property name="maximum">
-      <double>1000000.000000000000000</double>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QPushButton" name="multiply">
-     <property name="text">
-      <string>×</string>
-     </property>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>DoubleSpinBox</class>
-   <extends>QDoubleSpinBox</extends>
-   <header>widgets/doublespinbox.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/matrixeditor.cpp	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,89 @@
+#include "matrixeditor.h"
+#include "ui_matrixeditor.h"
+#include "multiplyfactordialog.h"
+
+constexpr char BUTTON_COLUMN_PROPERTY[] = "_ldforge_column";
+
+MatrixEditor::MatrixEditor(const glm::mat4 value, QWidget* parent) :
+	QWidget(parent),
+	ui(new Ui::MatrixEditor)
+{
+	ui->setupUi(this);
+	for (int column = 0; column < (int)glm::countof(this->spinboxes); column += 1)
+	{
+		for (int row = 0; row < (int)glm::countof(this->spinboxes[0]); row += 1)
+		{
+			const QString name = QStringLiteral("cell%1%2").arg(column).arg(row);
+			QDoubleSpinBox** spinbox = &this->spinboxes[column][row];
+			*spinbox = this->findChild<QDoubleSpinBox*>(name);
+			connect(*spinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), [&]()
+			{
+				Q_EMIT this->valueChanged(this->value());
+			});
+			Q_ASSERT(*spinbox != nullptr);
+		}
+		const QString multiplyButtonName = QStringLiteral("multiply%1").arg(column);
+		QAbstractButton* button = this->findChild<QAbstractButton*>(multiplyButtonName);
+		button->setProperty(BUTTON_COLUMN_PROPERTY, column);
+		connect(button, &QAbstractButton::clicked, this, &MatrixEditor::multiplyButtonPressed);
+	}
+	this->setValue(value);
+}
+
+MatrixEditor::MatrixEditor(QWidget *parent) :
+	MatrixEditor{glm::mat4{1}, parent}
+{
+}
+
+MatrixEditor::~MatrixEditor()
+{
+	delete ui;
+}
+
+glm::mat4 MatrixEditor::value() const
+{
+	glm::mat4 result{1};
+	for (int column = 0; column < (int)glm::countof(this->spinboxes); column += 1)
+	{
+		for (int row = 0; row < (int)glm::countof(this->spinboxes[0]); row += 1)
+		{
+			result[column][row] = static_cast<float>(this->spinboxes[column][row]->value());
+		}
+	}
+	return result;
+}
+
+void MatrixEditor::setValue(const glm::mat4& value)
+{
+	for (int column = 0; column < (int)glm::countof(this->spinboxes); column += 1)
+	{
+		for (int row = 0; row < (int)glm::countof(this->spinboxes[0]); row += 1)
+		{
+			QDoubleSpinBox* spinbox = this->spinboxes[column][row];
+			QSignalBlocker blocker{spinbox};
+			spinbox->setValue(value[column][row]);
+		}
+	}
+}
+
+void MatrixEditor::multiplyButtonPressed()
+{
+	QAbstractButton* button = qobject_cast<QAbstractButton*>(this->sender());
+	if (button != nullptr)
+	{
+		bool ok;
+		const int column = button->property(BUTTON_COLUMN_PROPERTY).toInt(&ok);
+		if (ok and column >= 0 and column < this->matrixSize())
+		{
+			glm::mat4 newValue = this->value();
+			MultiplyFactorDialog dialog{newValue[column], this};
+			const int result = dialog.exec();
+			if (result == QDialog::Accepted)
+			{
+				newValue[column] = glm::vec4{dialog.value(), (column == 3) ? 1 : 0};
+				this->setValue(newValue);
+				Q_EMIT valueChanged(newValue);
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/matrixeditor.h	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,30 @@
+#pragma once
+#include <QWidget>
+#include <glm/glm.hpp>
+
+namespace Ui {
+class MatrixEditor;
+}
+
+class MatrixEditor : public QWidget
+{
+	Q_OBJECT
+public:
+	explicit MatrixEditor(QWidget *parent = nullptr);
+	explicit MatrixEditor(const glm::mat4 value, QWidget* parent = nullptr);
+	~MatrixEditor();
+	glm::mat4 value() const;
+	void setValue(const glm::mat4& value);
+Q_SIGNALS:
+	void valueChanged(const glm::mat4& value);
+private:
+	constexpr int matrixSize() const;
+	Q_SLOT void multiplyButtonPressed();
+	class QDoubleSpinBox* spinboxes[4][3];
+	Ui::MatrixEditor *ui;
+};
+
+constexpr int MatrixEditor::matrixSize() const
+{
+	return 4;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/matrixeditor.ui	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MatrixEditor</class>
+ <widget class="QWidget" name="MatrixEditor">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>356</width>
+    <height>172</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="DoubleSpinBox" name="cell00"/>
+   </item>
+   <item row="0" column="1">
+    <widget class="DoubleSpinBox" name="cell10"/>
+   </item>
+   <item row="0" column="2">
+    <widget class="DoubleSpinBox" name="cell20"/>
+   </item>
+   <item row="0" column="3">
+    <widget class="DoubleSpinBox" name="cell30"/>
+   </item>
+   <item row="1" column="0">
+    <widget class="DoubleSpinBox" name="cell01"/>
+   </item>
+   <item row="1" column="1">
+    <widget class="DoubleSpinBox" name="cell11"/>
+   </item>
+   <item row="1" column="2">
+    <widget class="DoubleSpinBox" name="cell21"/>
+   </item>
+   <item row="1" column="3">
+    <widget class="DoubleSpinBox" name="cell31"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="DoubleSpinBox" name="cell02"/>
+   </item>
+   <item row="2" column="1">
+    <widget class="DoubleSpinBox" name="cell12"/>
+   </item>
+   <item row="2" column="2">
+    <widget class="DoubleSpinBox" name="cell22"/>
+   </item>
+   <item row="2" column="3">
+    <widget class="DoubleSpinBox" name="cell32"/>
+   </item>
+   <item row="3" column="0">
+    <widget class="DoubleSpinBox" name="doubleSpinBox">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="DoubleSpinBox" name="doubleSpinBox_2">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="2">
+    <widget class="DoubleSpinBox" name="doubleSpinBox_3">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="3">
+    <widget class="DoubleSpinBox" name="doubleSpinBox_4">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="value">
+      <double>1.000000000000000</double>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QPushButton" name="multiply0">
+     <property name="text">
+      <string>×</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QPushButton" name="multiply1">
+     <property name="text">
+      <string>×</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="2">
+    <widget class="QPushButton" name="multiply2">
+     <property name="text">
+      <string>×</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="3">
+    <widget class="QPushButton" name="multiply3">
+     <property name="text">
+      <string>×</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>DoubleSpinBox</class>
+   <extends>QDoubleSpinBox</extends>
+   <header>widgets/doublespinbox.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/multiplyfactordialog.cpp	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,102 @@
+#include "multiplyfactordialog.h"
+#include "ui_multiplyfactordialog.h"
+
+MultiplyFactorDialog::MultiplyFactorDialog(const glm::vec3& baseVector, QWidget* parent) :
+	QDialog{parent},
+	baseVector{baseVector},
+	preview{baseVector, parent, Vec3Editor::NoMultiplyButton}
+{
+	ui = std::make_unique<Ui::MultiplyFactorDialog>();
+	ui->setupUi(this);
+	this->preview.setEnabled(false);
+	this->ui->previewGroupBox->setLayout(new QVBoxLayout{parent});
+	this->ui->previewGroupBox->layout()->addWidget(&this->preview);
+	connect(
+		this->ui->invert,
+		&QCheckBox::clicked,
+		this,
+		&MultiplyFactorDialog::updatePreview);
+	connect(
+		this->ui->factor,
+		qOverload<double>(&DoubleSpinBox::valueChanged),
+		this,
+		&MultiplyFactorDialog::updatePreview);
+}
+
+/**
+ * @brief empty destructor, necessary because std::unique_ptr is used with a forward declaration
+ */
+MultiplyFactorDialog::~MultiplyFactorDialog()
+{
+}
+
+/**
+ * @brief Computes the resulting vector
+ * @return the input vector multiplied by the specified vector
+ */
+glm::vec3 MultiplyFactorDialog::value() const
+{
+	glm::vec3 result = baseVector;
+	if (this->ui->invert->isChecked())
+	{
+		if (qFuzzyIsNull(this->ui->factor->value()))
+		{
+			constexpr double infinity = std::numeric_limits<double>::quiet_NaN();
+			result = {infinity, infinity, infinity};
+		}
+		else
+		{
+			result /= this->ui->factor->value();
+		}
+	}
+	else
+	{
+		result *= this->ui->factor->value();
+	}
+	return result;
+}
+
+/**
+ * @brief Makes a string that is prefixed to the factor input.
+ * @param ui
+ * @return prefix string
+ */
+QString prefixForFactorInput(const Ui::MultiplyFactorDialog& ui)
+{
+	if (ui.invert->isChecked())
+	{
+		return "1 : ";
+	}
+	else
+	{
+		return "";
+	}
+}
+
+/**
+ * @brief Makes a string that is suffixed to the factor input.
+ * @param ui
+ * @return prefix string
+ */
+QString suffixForFactorInput(const Ui::MultiplyFactorDialog& ui)
+{
+	if (ui.invert->isChecked())
+	{
+		// render the actual factor that stuff gets effectively multiplied by
+		return " = " + QString::number(1.0 / (ui.factor->value()));
+	}
+	else
+	{
+		return "";
+	}
+}
+
+/**
+ * @brief Responds to changes in the value and updates previews accordingly
+ */
+void MultiplyFactorDialog::updatePreview()
+{
+	this->ui->factor->setPrefix(::prefixForFactorInput(*this->ui));
+	this->ui->factor->setSuffix(::suffixForFactorInput(*this->ui));
+	this->preview.setValue(this->value());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/multiplyfactordialog.h	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,22 @@
+#pragma once
+#include <QDialog>
+#include "vec3editor.h"
+
+namespace Ui
+{
+	class MultiplyFactorDialog;
+}
+
+class MultiplyFactorDialog : public QDialog
+{
+	Q_OBJECT
+public:
+	explicit MultiplyFactorDialog(const glm::vec3& baseVector = glm::vec3{}, QWidget *parent = nullptr);
+	~MultiplyFactorDialog();
+	glm::vec3 value() const;
+private:
+	Q_SLOT void updatePreview();
+	std::unique_ptr<Ui::MultiplyFactorDialog> ui;
+	const glm::vec3 baseVector;
+	Vec3Editor preview;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/multiplyfactordialog.ui	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MultiplyFactorDialog</class>
+ <widget class="QDialog" name="MultiplyFactorDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>286</width>
+    <height>169</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Multiply with a scalar</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QFormLayout" name="formLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Factor:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="DoubleSpinBox" name="factor">
+       <property name="value">
+        <double>1.000000000000000</double>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QCheckBox" name="invert">
+       <property name="text">
+        <string>Invert</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="previewGroupBox">
+     <property name="title">
+      <string>Preview</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>DoubleSpinBox</class>
+   <extends>QDoubleSpinBox</extends>
+   <header>widgets/doublespinbox.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>MultiplyFactorDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>MultiplyFactorDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/vec3editor.cpp	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,67 @@
+#include <QDialog>
+#include <QCheckBox>
+#include <QSignalBlocker>
+#include "vec3editor.h"
+#include "ui_vec3editor.h"
+#include "multiplyfactordialog.h"
+
+Vec3Editor::Vec3Editor(const glm::vec3& value, QWidget *parent, QFlags<Flag> flags) :
+	QWidget{parent},
+	ui{new Ui::Vec3Editor}
+{
+	this->ui->setupUi(this);
+	this->setValue(value);
+	if (flags.testFlag(NoMultiplyButton))
+	{
+		this->ui->multiply->setVisible(false);
+	}
+	else
+	{
+		connect(this->ui->multiply, &QPushButton::clicked, this, &Vec3Editor::multiplyPressed);
+	}
+	for (QDoubleSpinBox* spinbox : this->spinboxes())
+	{
+		connect(spinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), [&](double)
+		{
+			Q_EMIT this->valueChanged(this->value());
+		});
+	}
+}
+
+Vec3Editor::~Vec3Editor()
+{
+}
+
+glm::vec3 Vec3Editor::value() const
+{
+	auto get = [](DoubleSpinBox* spinbox){ return static_cast<float>(spinbox->value()); };
+	return {get(this->ui->x), get(this->ui->y), get(this->ui->z)};
+}
+
+void Vec3Editor::setValue(const glm::vec3& value)
+{
+	auto set = [](DoubleSpinBox* spinbox, float value)
+	{
+		QSignalBlocker blocker{spinbox};
+		spinbox->setValue(static_cast<qreal>(value));
+	};
+	set(this->ui->x, value.x);
+	set(this->ui->y, value.y);
+	set(this->ui->z, value.z);
+	Q_EMIT this->valueChanged(value);
+}
+
+std::array<DoubleSpinBox*, 3> Vec3Editor::spinboxes()
+{
+	return {this->ui->x, this->ui->y, this->ui->z};
+}
+
+void Vec3Editor::multiplyPressed()
+{
+	MultiplyFactorDialog dialog{this->value(), this};
+	const int dialogResult = dialog.exec();
+	if (dialogResult == QDialog::Accepted)
+	{
+		this->setValue(dialog.value());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/vec3editor.h	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,30 @@
+#pragma once
+#include <QWidget>
+#include <glm/glm.hpp>
+
+namespace Ui
+{
+	class Vec3Editor;
+}
+
+class Vec3Editor : public QWidget
+{
+	Q_OBJECT
+public:
+	enum Flag
+	{
+		NoMultiplyButton = 0x1
+	};
+	explicit Vec3Editor(const glm::vec3& value, QWidget* parent = nullptr, QFlags<Flag> flags = {});
+	~Vec3Editor();
+	glm::vec3 value() const;
+	void setValue(const glm::vec3& value);
+Q_SIGNALS:
+	void valueChanged(const glm::vec3& value);
+private:
+	std::array<class DoubleSpinBox*, 3> spinboxes();
+	Q_SLOT void multiplyPressed();
+	std::unique_ptr<Ui::Vec3Editor> ui;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags<Vec3Editor::Flag>)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/vec3editor.ui	Wed Jun 22 20:27:53 2022 +0300
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Vec3Editor</class>
+ <widget class="QWidget" name="Vec3Editor">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>613</width>
+    <height>46</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1,1,0">
+   <item>
+    <widget class="DoubleSpinBox" name="x">
+     <property name="prefix">
+      <string>x = </string>
+     </property>
+     <property name="decimals">
+      <number>4</number>
+     </property>
+     <property name="minimum">
+      <double>-1000000.000000000000000</double>
+     </property>
+     <property name="maximum">
+      <double>1000000.000000000000000</double>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="DoubleSpinBox" name="y">
+     <property name="prefix">
+      <string>y = </string>
+     </property>
+     <property name="decimals">
+      <number>4</number>
+     </property>
+     <property name="minimum">
+      <double>-1000000.000000000000000</double>
+     </property>
+     <property name="maximum">
+      <double>1000000.000000000000000</double>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="DoubleSpinBox" name="z">
+     <property name="prefix">
+      <string>z = </string>
+     </property>
+     <property name="decimals">
+      <number>4</number>
+     </property>
+     <property name="minimum">
+      <double>-1000000.000000000000000</double>
+     </property>
+     <property name="maximum">
+      <double>1000000.000000000000000</double>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QPushButton" name="multiply">
+     <property name="text">
+      <string>×</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>DoubleSpinBox</class>
+   <extends>QDoubleSpinBox</extends>
+   <header>widgets/doublespinbox.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>

mercurial