begin refactor of gl side

Sun, 12 Jun 2022 20:47:04 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Sun, 12 Jun 2022 20:47:04 +0300
changeset 214
8e1fe64ce4e3
parent 213
ee5758ddb6d2
child 215
34c6e7bc4ee1

begin refactor of gl side

CMakeLists.txt file | annotate | diff | comparison | revisions
src/basics.h file | annotate | diff | comparison | revisions
src/document.cpp file | annotate | diff | comparison | revisions
src/document.h file | annotate | diff | comparison | revisions
src/document.ui file | annotate | diff | comparison | revisions
src/documentmanager.cpp file | annotate | diff | comparison | revisions
src/documentmanager.h file | annotate | diff | comparison | revisions
src/gl/vertexprogram.cpp file | annotate | diff | comparison | revisions
src/gl/vertexprogram.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/ui/canvas.cpp file | annotate | diff | comparison | revisions
src/ui/canvas.h file | annotate | diff | comparison | revisions
src/widgets/colorindexinput.cpp file | annotate | diff | comparison | revisions
src/widgets/colorindexinput.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Sat Jun 11 15:20:24 2022 +0300
+++ b/CMakeLists.txt	Sun Jun 12 20:47:04 2022 +0300
@@ -95,7 +95,6 @@
 	src/widgets/vec3editor.h
 )
 set (LDFORGE_FORMS
-	src/document.ui
 	src/mainwindow.ui
 	src/settingseditor/librarieseditor.ui
 	src/settingseditor/settingseditor.ui
--- a/src/basics.h	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/basics.h	Sun Jun 12 20:47:04 2022 +0300
@@ -308,8 +308,8 @@
 	return qHash(key.x) ^ rotl10(qHash(key.y)) ^ rotl20(qHash(key.z));
 }
 
-template<typename K, typename V, typename Fn>
-void forValueInMap(const std::map<K, V>& map, Fn&& fn)
+template<typename T, typename Fn>
+void forValueInMap(T&& map, Fn&& fn)
 {
 	for (const auto& it : map) {
 		fn(it.second);
--- a/src/document.cpp	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/document.cpp	Sun Jun 12 20:47:04 2022 +0300
@@ -23,41 +23,38 @@
 #include "model.h"
 #include "ui/objecteditor.h"
 
-EditorTabWidget::EditorTabWidget(
+EditTools::EditTools(
 	Model* model,
-	DocumentManager* documents,
 	const ColorTable& colorTable,
-	QWidget* parent) :
-	QWidget{parent},
+	QObject* parent) :
+	QObject{parent},
 	colorTable{colorTable},
-	canvas{new Canvas{model, this, documents, colorTable, this}},
 	model{model},
 	vertexMap{model}
 {
-	this->setMouseTracking(true);
-	connect(this->canvas, &Canvas::mouseClick, this, &EditorTabWidget::canvasMouseClick);
-	connect(this->canvas, &Canvas::mouseMove, this, &EditorTabWidget::canvasMouseMove);
-	connect(this->canvas, &Canvas::newStatusText, this, &EditorTabWidget::newStatusText);
+#if 0
+	connect(this->canvas, &Canvas::mouseClick, this, &EditTools::canvasMouseClick);
+	connect(this->canvas, &Canvas::mouseMove, this, &EditTools::canvasMouseMove);
+	connect(this->canvas, &Canvas::newStatusText, this, &EditTools::newStatusText);
 	connect(this->model, &Model::dataChanged, this->canvas, qOverload<>(&Canvas::update));
 	connect(&this->vertexMap, &VertexMap::verticesChanged, [&]()
 	{
-		this->canvas->rebuildVertices(this);
+		this->canvas->rebuildVertices(&this->vertexMap);
 	});
 	this->canvas->drawState = &this->drawState;
-	QVBoxLayout* layout = new QVBoxLayout{this};
-	layout->addWidget(this->canvas);
+#endif
 }
 
-EditorTabWidget::~EditorTabWidget()
+EditTools::~EditTools()
 {
 }
 
-void EditorTabWidget::applyToVertices(VertexMap::ApplyFunction fn) const
+void EditTools::applyToVertices(VertexMap::ApplyFunction fn) const
 {
 	this->vertexMap.apply(fn);
 }
 
-void EditorTabWidget::setEditMode(EditingMode mode)
+void EditTools::setEditMode(EditingMode mode)
 {
 	this->drawState.mode = mode;
 }
@@ -87,8 +84,9 @@
 	return any(points, std::bind(isclose, std::placeholders::_1, pos));
 }
 
-void EditorTabWidget::canvasMouseClick(QMouseEvent *event)
+void EditTools::canvasMouseClick(QMouseEvent*)
 {
+#if 0
 	switch(this->drawState.mode)
 	{
 	case SelectMode:
@@ -104,15 +102,11 @@
 		}
 		break;
 	case DrawMode:
-		if (event->button() == Qt::LeftButton and this->canvas->worldPosition.has_value())
-		{
-			const glm::vec3& pos = this->canvas->worldPosition.value();
-			if (isCloseToExistingPoints(this->drawState.polygon, pos))
-			{
+		if (event->button() == Qt::LeftButton) {
+			if (isCloseToExistingPoints(this->drawState.polygon, worldPosition)) {
 				this->closeShape();
 			}
-			else
-			{
+			else {
 				this->drawState.polygon.push_back(pos);
 				updatePreviewPolygon(&this->drawState);
 			}
@@ -128,10 +122,11 @@
 		}
 		break;
 	}
+#endif
 }
-
-void EditorTabWidget::canvasMouseMove(QMouseEvent *event)
+void EditTools::canvasMouseMove(QMouseEvent*)
 {
+#if 0
 	switch(this->drawState.mode)
 	{
 	case SelectMode:
@@ -146,7 +141,9 @@
 		event->accept();
 		break;
 	}
+#endif
 }
+#if 0
 /*
 
 void EditorTabWidget::select(const QSet<ModelId> &selected)
@@ -165,17 +162,17 @@
 	selectionModel->select(itemSelection, QItemSelectionModel::ClearAndSelect);
 }
 */
-const QSet<ModelId> EditorTabWidget::selectedObjects() const
+const QSet<ModelId> EditTools::selectedObjects() const
 {
 	return this->canvas->selectedObjects();
 }
-
-EditingMode EditorTabWidget::currentEditingMode() const
+#endif
+EditingMode EditTools::currentEditingMode() const
 {
 	return this->drawState.mode;
 }
-
-void EditorTabWidget::closeShape()
+#if 0
+void EditTools::closeShape()
 {
 	if (this->drawState.polygon.size() >= 2 and this->drawState.polygon.size() <= 4)
 	{
@@ -222,3 +219,5 @@
 	this->drawState.polygon.clear();
 	updatePreviewPolygon(&this->drawState);
 }
+
+#endif
--- a/src/document.h	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/document.h	Sun Jun 12 20:47:04 2022 +0300
@@ -38,20 +38,18 @@
 
 Q_DECLARE_METATYPE(ModelAction)
 
-class EditorTabWidget : public QWidget
+class EditTools : public QObject
 {
 	Q_OBJECT
 public:
-	explicit EditorTabWidget(
+	explicit EditTools(
 		Model* model,
-		DocumentManager* documents,
 		const ColorTable& colorTable,
-		QWidget *parent = nullptr);
-	~EditorTabWidget() override;
+		QObject *parent = nullptr);
+	~EditTools() override;
 	void applyToVertices(VertexMap::ApplyFunction fn) const;
 	const QSet<ModelId> selectedObjects() const;
 	const ColorTable& colorTable;
-	Canvas* const canvas;
 	Model* const model;
 	EditingMode currentEditingMode() const;
 	Q_SLOT void setEditMode(EditingMode mode);
@@ -61,6 +59,7 @@
 	void newStatusText(const QString& newStatusText);
 	void splitterChanged();
 	void modelAction(const ModelAction& action);
+	void drawStateChanged(const DrawState& drawState);
 private:
 	void closeShape();
 	DrawState drawState;
--- a/src/document.ui	Sat Jun 11 15:20:24 2022 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Document</class>
- <widget class="QWidget" name="Document">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>957</width>
-    <height>540</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
-   <item>
-    <widget class="QSplitter" name="viewportListSplitter">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <widget class="QFrame" name="viewportFrame">
-      <property name="frameShape">
-       <enum>QFrame::StyledPanel</enum>
-      </property>
-      <property name="frameShadow">
-       <enum>QFrame::Raised</enum>
-      </property>
-     </widget>
-     <widget class="QListView" name="listView">
-      <property name="alternatingRowColors">
-       <bool>true</bool>
-      </property>
-      <property name="selectionMode">
-       <enum>QAbstractItemView::ExtendedSelection</enum>
-      </property>
-      <property name="selectionBehavior">
-       <enum>QAbstractItemView::SelectRows</enum>
-      </property>
-     </widget>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
--- a/src/documentmanager.cpp	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/documentmanager.cpp	Sun Jun 12 20:47:04 2022 +0300
@@ -24,7 +24,8 @@
 #include "documentmanager.h"
 #include "parser.h"
 
-DocumentManager::DocumentManager()
+DocumentManager::DocumentManager(QObject *parent) :
+	QObject{parent}
 {
 }
 
@@ -35,8 +36,10 @@
 ModelId DocumentManager::newModel()
 {
 	const ModelId modelId{++this->modelIdCounter};
-	this->openModels[modelId].id = modelId;
-	this->openModels[modelId].opentype = OpenType::ManuallyOpened;
+	this->openModels.emplace(std::make_pair(modelId, ModelInfo{
+		.id = modelId,
+		.opentype = OpenType::ManuallyOpened,
+	}));
 	this->makePolygonCacheForModel(modelId);
 	return modelId;
 }
@@ -122,13 +125,13 @@
 	if (file.error() == QFile::NoError)
 	{
 		const ModelId modelId{++this->modelIdCounter};
-		this->openModels[modelId] = {
+		this->openModels.emplace(std::make_pair(modelId, ModelInfo{
 			.model = std::move(newModel),
 			.id = modelId,
 			.path = path,
 			.opentype = openType,
 			.polygonCache = {},
-		};
+		}));
 		this->makePolygonCacheForModel(modelId);
 		result = modelId;
 	}
@@ -241,11 +244,20 @@
 	}
 }
 
-const DocumentManager::ModelInfo *DocumentManager::infoForModel(ModelId modelId) const
+const DocumentManager::ModelInfo *DocumentManager::find(ModelId modelId) const
 {
 	return findInMap(this->openModels, modelId);
 }
 
+void DocumentManager::setModelPayload(ModelId modelId, QObject *object)
+{
+	ModelInfo* info = findInMap(this->openModels, modelId);
+	if (info != nullptr) {
+		info->payload = object;
+		object->setParent(this);
+	}
+}
+
 QString errorStringFromMissingDependencies(const DocumentManager::MissingDependencies& missing)
 {
 	QString missingString;
--- a/src/documentmanager.h	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/documentmanager.h	Sun Jun 12 20:47:04 2022 +0300
@@ -29,8 +29,9 @@
 	AutomaticallyOpened,
 };
 
-class DocumentManager
+class DocumentManager : public QObject
 {
+	Q_OBJECT
 public:
 	struct ModelInfo
 	{
@@ -39,10 +40,13 @@
 		QString path;
 		OpenType opentype;
 		std::map<QString, ModelId> dependencies = {};
-		PolygonCache polygonCache;
+		PolygonCache polygonCache = {};
+		QObject* payload;
 	};
 	using MissingDependencies = std::map<QString, QString>;
-	DocumentManager();
+	DocumentManager(QObject* parent = nullptr);
+	auto begin() const { return this->openModels.begin(); }
+	auto end() const { return this->openModels.end(); }
 	ModelId newModel();
 	Model* findDependencyByName(const ModelId modelId, const QString& name);
 	Model* getModelById(ModelId modelId);
@@ -58,7 +62,14 @@
 	bool saveModel(const ModelId modelId, QTextStream& errors);
 	std::optional<ModelId> findIdForModel(const Model* model) const;
 	PolygonCache* getPolygonCacheForModel(ModelId modelId);
-	const ModelInfo* infoForModel(ModelId modelId) const;
+	const ModelInfo* find(ModelId modelId) const;
+	void setModelPayload(ModelId modelId, QObject* object);
+	template<typename T>
+	T* findPayload(ModelId modelId) const
+	{
+		const ModelInfo* info = this->find(modelId);
+		return info ? qobject_cast<T*>(info->payload) : nullptr;
+	}
 private:
 	int modelIdCounter = 0;
 	std::map<ModelId, ModelInfo> openModels;
--- a/src/gl/vertexprogram.cpp	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/gl/vertexprogram.cpp	Sun Jun 12 20:47:04 2022 +0300
@@ -120,12 +120,12 @@
 	this->fragmentStyle = newFragmentStyle;
 }
 
-void VertexProgram::build(const EditorTabWidget *document)
+void VertexProgram::build(const VertexMap* vertexMap)
 {
 	constexpr glm::vec3 color = {0.0, 1.0, 1.0};
 	this->data.clear();
 	const std::vector<glm::vec3> sphere = ::sphere(8 / 2);
-	document->applyToVertices([&](const glm::vec3&, const VertexMap::VertexInfo& info)
+	vertexMap->apply([&](const glm::vec3&, const VertexMap::VertexInfo& info)
 	{
 		reserveMore(this->data, sphere.size());
 		for (const glm::vec3& point : sphere)
--- a/src/gl/vertexprogram.h	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/gl/vertexprogram.h	Sun Jun 12 20:47:04 2022 +0300
@@ -1,7 +1,7 @@
 #ifndef VERTEXPROGRAM_H
 #define VERTEXPROGRAM_H
 #include "basicshaderprogram.h"
-class EditorTabWidget;
+class VertexMap;
 
 class VertexProgram : public AbstractBasicShaderProgram
 {
@@ -17,7 +17,7 @@
 		glm::vec3 color;
 	};
 	VertexProgram(QObject* parent = nullptr);
-	void build(const EditorTabWidget* document);
+	void build(const VertexMap* document);
 protected:
 	const char* vertexShaderSource() const override;
 	const char* fragmentShaderSource() const override;
--- a/src/main.cpp	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/main.cpp	Sun Jun 12 20:47:04 2022 +0300
@@ -3,15 +3,34 @@
 #include <QMessageBox>
 #include <QMdiSubWindow>
 #include <QStackedWidget>
+#include <QCloseEvent>
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 #include "version.h"
+#include "ui/canvas.h"
 #include "document.h"
 #include "settingseditor/settingseditor.h"
 #include "widgets/colorselectdialog.h"
 
 static const QDir LOCALE_DIR {":/locale"};
 
+class ModelSubWindow : public QMdiSubWindow
+{
+	Q_OBJECT
+public:
+	const ModelId modelId;
+	ModelSubWindow(ModelId modelId, QWidget* widget) :
+		QMdiSubWindow{widget},
+		modelId{modelId}
+	{
+	}
+protected:
+	void closeEvent(QCloseEvent* event) override
+	{
+		event->ignore();
+	}
+};
+
 static void doQtRegistrations()
 {
 	QCoreApplication::setApplicationName(::appName);
@@ -32,6 +51,18 @@
 	}
 };
 
+class ModelData : public QObject
+{
+	Q_OBJECT
+public:
+	ModelData(QObject* parent) : QObject {parent} {}
+	std::unique_ptr<Canvas> canvas;
+	std::unique_ptr<QItemSelectionModel> itemSelectionModel;
+	std::unique_ptr<EditTools> tools;
+	Model* model;
+};
+#include "main.moc"
+
 static constexpr MemberData<Ui_MainWindow, QAction*, gl::RenderStyle> renderStyleButtons[] = {
 	{ offsetof(Ui_MainWindow, actionRenderStyleNormal), gl::RenderStyle::Normal },
 	{ offsetof(Ui_MainWindow, actionRenderStyleBfc), gl::RenderStyle::BfcRedGreen },
@@ -107,52 +138,41 @@
 	}
 }
 
-/*
-void MainWindow::handleDocumentSplitterChange()
+ModelData* findModelData(DocumentManager* documents, ModelId modelId)
+{
+	return documents->findPayload<ModelData>(modelId);
+}
+
+static ModelSubWindow* currentModelSubWindow(Ui_MainWindow* ui)
 {
-	EditorTabWidget* currentDocument = this->currentDocument();
-	if (currentDocument != nullptr)
-	{
-		this->documentSplitterState = currentDocument->saveSplitterState();
-		for (int i = 0; i < this->ui->tabs->count(); i += 1)
-		{
-			EditorTabWidget* document = qobject_cast<EditorTabWidget*>(this->ui->tabs->widget(i));
-			if (document != nullptr and document != currentDocument)
-			{
-				document->restoreSplitterState(this->documentSplitterState);
-			}
-		}
-		this->settings.setMainSplitterState(this->documentSplitterState);
-	}
+	return qobject_cast<ModelSubWindow*>(ui->mdiArea->activeSubWindow());
 }
-*/
 
-static EditorTabWidget* currentTabWidget(Ui_MainWindow* ui)
+static ModelData* currentModelData(Ui_MainWindow* ui, DocumentManager* documents)
 {
-	QMdiSubWindow* activeSubWindow = ui->mdiArea->activeSubWindow();
-	if (activeSubWindow == nullptr) {
-		return nullptr;
+	if (auto* const activeSubWindow = currentModelSubWindow(ui)) {
+		return findModelData(documents, activeSubWindow->modelId);
 	}
 	else {
-		return qobject_cast<EditorTabWidget*>(activeSubWindow->widget());
-	}
-};
-
-
-static void closeDocument(DocumentManager* documents, EditorTabWidget *document)
-{
-	std::optional<ModelId> modelId = documents->findIdForModel(document->model);
-	if (modelId.has_value()) {
-		documents->closeDocument(modelId.value());
-		delete document;
+		return nullptr;
 	}
 }
 
-static std::optional<ModelId> findCurrentModelId(Ui_MainWindow* ui, DocumentManager* documents)
+static Model* currentModelBody(Ui_MainWindow* ui, DocumentManager* documents)
 {
-	const EditorTabWidget* tab = currentTabWidget(ui);
-	if (tab != nullptr) {
-		return documents->findIdForModel(tab->model);
+	if (auto* const activeSubWindow = currentModelSubWindow(ui)) {
+		return documents->getModelById(activeSubWindow->modelId);
+	}
+	else {
+		return nullptr;
+	}
+}
+
+static std::optional<ModelId> findCurrentModelId(Ui_MainWindow* ui)
+{
+	ModelSubWindow* activeSubWindow = qobject_cast<ModelSubWindow*>(ui->mdiArea->activeSubWindow());
+	if (activeSubWindow != nullptr) {
+		return activeSubWindow->modelId;
 	}
 	else {
 		return {};
@@ -196,16 +216,28 @@
 	}
 }
 
+template<typename Fn>
+static void forModel(const DocumentManager* documents, Fn&& fn)
+{
+	forValueInMap(*documents, [&fn](const DocumentManager::ModelInfo& info)
+	{
+		ModelData* modelSpecificData = qobject_cast<ModelData*>(info.payload);
+		if (modelSpecificData != nullptr) {
+			fn(&info, modelSpecificData);
+		}
+	});
+}
+
 static void updateRenderPreferences(
 	Ui_MainWindow* ui,
-	const gl::RenderPreferences* renderPreferences)
+	const gl::RenderPreferences* renderPreferences,
+	const DocumentManager* documents)
 {
-	for (QMdiSubWindow* subWindow : ui->mdiArea->subWindowList()) {
-		EditorTabWidget* tab = qobject_cast<EditorTabWidget*>(subWindow->widget());
-		if (tab != nullptr) {
-			tab->canvas->setRenderPreferences(*renderPreferences);
+	forModel(documents, [&renderPreferences](const void*, const ModelData* data){
+		if (data->canvas != nullptr) {
+			data->canvas->setRenderPreferences(*renderPreferences);
 		}
-	}
+	});
 	for (auto data : ::renderStyleButtons) {
 		QAction* action = data.memberInstance(ui);
 		action->setChecked(renderPreferences->style == data.payload);
@@ -295,7 +327,6 @@
 	QStringList recentlyOpenedFiles;
 	ColorTable colorTable;
 	gl::RenderPreferences renderPreferences;
-	QMap<Model*, QItemSelectionModel*> itemSelectionModels;
 	ui.setupUi(&mainWindow);
 	const uiutilities::KeySequenceMap defaultKeyboardShortcuts =
 		uiutilities::makeKeySequenceMap(uiutilities::collectActions(&mainWindow));
@@ -340,7 +371,7 @@
 		libraries.restoreFromSettings(&settings);
 		updateRecentlyOpenedDocumentsMenu();
 		colorTable = loadColors(&libraries);
-		updateRenderPreferences(&ui, &renderPreferences);
+		updateRenderPreferences(&ui, &renderPreferences, &documents);
 		ui.mdiArea->setViewMode(static_cast<QMdiArea::ViewMode>(settings.viewMode()));
 		ui.retranslateUi(&mainWindow);
 	};
@@ -357,36 +388,45 @@
 	};
 	const auto openModelForEditing = [&](const ModelId modelId){
 		Model* model = documents.getModelById(modelId);
-		EditorTabWidget* document = new EditorTabWidget{model, &documents, colorTable};
-		QObject::connect(
-			document,
-			&EditorTabWidget::modelAction,
-			std::bind(executeAction, model, std::placeholders::_1));
-		QItemSelectionModel* selectionModel = new QItemSelectionModel{model};
-		itemSelectionModels[model] = selectionModel;
-		QObject::connect(selectionModel, &QItemSelectionModel::selectionChanged,
-			[model, document](const QItemSelection& selected, const QItemSelection& deselected)
-		{
-			auto resolveIndex = [&model](const QModelIndex& index){
-				return model->idAt(index.row());
-			};
-			auto resolve = [&resolveIndex](const QItemSelection& selection)
+		if (model != nullptr) {
+			ModelData* data = new ModelData(&documents);
+			data->tools = std::make_unique<EditTools>(model, colorTable);
+			data->canvas = std::make_unique<Canvas>(model, data->tools.get(), &documents, colorTable);
+			data->itemSelectionModel = std::make_unique<QItemSelectionModel>(model);
+			data->model = model;
+			QObject::connect(
+				data->tools.get(),
+				&EditTools::modelAction,
+				std::bind(executeAction, model, std::placeholders::_1));
+			QObject::connect(data->itemSelectionModel.get(), &QItemSelectionModel::selectionChanged,
+				[modelId, &documents](
+				const QItemSelection& selected,
+				const QItemSelection& deselected)
 			{
-				return fn::map<QSet<ModelId>>(selection.indexes(), resolveIndex);
-			};
-			document->canvas->handleSelectionChange(resolve(selected), resolve(deselected));
-		});
-		document->canvas->setRenderPreferences(renderPreferences);
-		QObject::connect(
-			document,
-			&EditorTabWidget::newStatusText,
-			[&](const QString& newStatusText) {
-				mainWindow.statusBar()->showMessage(newStatusText);
+				ModelData* data = findModelData(&documents, modelId);
+				if (data != nullptr) {
+					auto resolveIndex = [&data](const QModelIndex& index){
+						return data->model->idAt(index.row());
+					};
+					auto resolve = [&resolveIndex](const QItemSelection& selection)
+					{
+						return fn::map<QSet<ModelId>>(selection.indexes(), resolveIndex);
+					};
+					data->canvas->handleSelectionChange(resolve(selected), resolve(deselected));
+				}
 			});
-		const QFileInfo fileInfo{*documents.modelPath(modelId)};
-		QMdiSubWindow* subWindow = ui.mdiArea->addSubWindow(document);
-		subWindow->setWindowTitle(tabName(fileInfo));
-		subWindow->show();
+			data->canvas->setRenderPreferences(renderPreferences);
+			QObject::connect(
+				data->tools.get(),
+				&EditTools::newStatusText,
+				[&](const QString& newStatusText) {
+					mainWindow.statusBar()->showMessage(newStatusText);
+				});
+			const QFileInfo fileInfo{*documents.modelPath(modelId)};
+			QMdiSubWindow* subWindow = ui.mdiArea->addSubWindow(data->canvas.get());
+			subWindow->setWindowTitle(tabName(fileInfo));
+			subWindow->show();
+		}
 	};
 	QObject::connect(ui.actionNew, &QAction::triggered, [&]{
 		openModelForEditing(documents.newModel());
@@ -411,18 +451,14 @@
 		}
 	});
 	QObject::connect(ui.actionQuit, &QAction::triggered, &mainWindow, &QMainWindow::close);
-	QObject::connect(ui.actionAdjustGridToView, &QAction::triggered, [&ui]{
-		EditorTabWidget* tab = currentTabWidget(&ui);
-		if (tab != nullptr)
-		{
-			adjustGridToView(tab->canvas);
+	QObject::connect(ui.actionAdjustGridToView, &QAction::triggered, [&]{
+		if (ModelData* data = currentModelData(&ui, &documents)) {
+			adjustGridToView(data->canvas.get());
 		}
 	});
 	QObject::connect(ui.actionClose, &QAction::triggered, [&ui, &documents]{
-		EditorTabWidget* tab = currentTabWidget(&ui);
-		if (tab != nullptr)
-		{
-			closeDocument(&documents, tab);
+		if (ModelData* data = currentModelData(&ui, &documents)) {
+			// TODO
 		}
 	});
 	const auto save = [&](ModelId modelId){
@@ -442,7 +478,7 @@
 		}
 	};
 	const auto actionSaveAs = [&]{
-		const std::optional<ModelId> modelId = findCurrentModelId(&ui, &documents);
+		const std::optional<ModelId> modelId = findCurrentModelId(&ui);
 		if (modelId.has_value())
 		{
 			const QString* pathPtr = documents.modelPath(*modelId);
@@ -467,40 +503,37 @@
 	};
 	QObject::connect(ui.actionSaveAs, &QAction::triggered, actionSaveAs);
 	QObject::connect(ui.actionSave, &QAction::triggered, [&]{
-		if (currentTabWidget(&ui) != nullptr) {
-			const std::optional<ModelId> modelId = findCurrentModelId(&ui, &documents);
-			if (modelId.has_value()) {
-				const QString* path = documents.modelPath(*modelId);
-				if (path == nullptr or path->isEmpty()) {
-					actionSaveAs();
-				}
-				else {
-					save(*modelId);
-				}
+		const std::optional<ModelId> modelId = findCurrentModelId(&ui);
+		if (modelId.has_value()) {
+			const QString* path = documents.modelPath(*modelId);
+			if (path == nullptr or path->isEmpty()) {
+				actionSaveAs();
+			}
+			else {
+				save(*modelId);
 			}
 		}
 	});
 	QObject::connect(ui.actionDelete, &QAction::triggered, [&]{
-		EditorTabWidget* tab = currentTabWidget(&ui);
-		if (tab != nullptr) {
+		if (Model* model = currentModelBody(&ui, &documents)) {
 			std::vector<int> selectedRows = rows(ui.modelListView->selectionModel()->selectedRows());
 			std::sort(selectedRows.begin(), selectedRows.end(), std::greater<int>{});
 			for (int row : selectedRows) {
-				executeAction(tab->model, DeleteFromModel{.position = row});
+				executeAction(model, DeleteFromModel{.position = row});
 			}
 		}
 	});
 	QObject::connect(ui.actionDrawAxes, &QAction::triggered, [&](bool drawAxes){
 		renderPreferences.drawAxes = drawAxes;
 		saveSettings();
-		updateRenderPreferences(&ui, &renderPreferences);
+		updateRenderPreferences(&ui, &renderPreferences, &documents);
 	});
 	for (auto data : ::renderStyleButtons) {
 		QAction* action = data.memberInstance(&ui);
 		QObject::connect(action, &QAction::triggered, [&, data]{
 			renderPreferences.style = data.payload;
 			saveSettings();
-			updateRenderPreferences(&ui, &renderPreferences);
+			updateRenderPreferences(&ui, &renderPreferences, &documents);
 		});
 	}
 	const auto checkEditingModeAction = [&ui](EditingMode mode) {
@@ -511,24 +544,22 @@
 	initializeTools(&ui, &mainWindow);
 	for (QAction* action : ui.editingModesToolBar->actions()) {
 		QObject::connect(action, &QAction::triggered, [&, action]{
-			EditorTabWidget* tab = currentTabWidget(&ui);
-			if (tab != nullptr) {
+			if (ModelData* data = currentModelData(&ui, &documents)) {
 				const EditingMode mode = action->data().value<EditingMode>();
-				tab->setEditMode(mode);
+				data->tools->setEditMode(mode);
 				checkEditingModeAction(mode);
 			}
 		});
 	}
 	QObject::connect(ui.mdiArea, &QMdiArea::subWindowActivated,
 	[&](QMdiSubWindow* subWindow) {
-		if (subWindow != nullptr) {
-			EditorTabWidget* tab = qobject_cast<EditorTabWidget*>(subWindow->widget());
-			if (tab != nullptr) {
-				checkEditingModeAction(tab->currentEditingMode());
-				QItemSelectionModel* selectionModel = itemSelectionModels.value(tab->model);
-				if (selectionModel != nullptr) {
-					ui.modelListView->setModel(tab->model);
-					ui.modelListView->setSelectionModel(selectionModel);
+		ModelSubWindow* modelSubWindow = qobject_cast<ModelSubWindow*>(subWindow);
+		if (modelSubWindow != nullptr) {
+			if (ModelData* data = documents.findPayload<ModelData>(modelSubWindow->modelId)) {
+				checkEditingModeAction(data->tools->currentEditingMode());
+				if (data->itemSelectionModel != nullptr) {
+					ui.modelListView->setModel(data->model);
+					ui.modelListView->setSelectionModel(data->itemSelectionModel.get());
 				}
 			}
 		}
@@ -536,7 +567,7 @@
 	mainWindow.setWindowTitle(title());
 	mainWindow.restoreGeometry(settings.mainWindowGeometry());
 	restoreSettings();
-	updateRenderPreferences(&ui, &renderPreferences);
+	updateRenderPreferences(&ui, &renderPreferences, &documents);
 	mainWindow.show();
 	const int result = app.exec();
 	saveSettings();
--- a/src/ui/canvas.cpp	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/ui/canvas.cpp	Sun Jun 12 20:47:04 2022 +0300
@@ -5,7 +5,7 @@
 
 Canvas::Canvas(
 	Model* model,
-	EditorTabWidget *document,
+	EditTools *document,
 	DocumentManager* documents,
 	const ColorTable& colorTable,
 	QWidget* parent) :
@@ -33,11 +33,11 @@
  * @brief Updates vertex rendering
  * @param document Document to get vertices from
  */
-void Canvas::rebuildVertices(EditorTabWidget* document)
+void Canvas::rebuildVertices(VertexMap* vertexMap)
 {
 	if (this->vertexProgram.has_value())
 	{
-		this->vertexProgram->build(document);
+		this->vertexProgram->build(vertexMap);
 		this->update();
 	}
 }
--- a/src/ui/canvas.h	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/ui/canvas.h	Sun Jun 12 20:47:04 2022 +0300
@@ -7,6 +7,8 @@
 #include "gl/axesprogram.h"
 #include "gl/vertexprogram.h"
 
+class EditTools;
+
 enum EditingMode
 {
 	SelectMode,
@@ -30,7 +32,7 @@
 	using OverpaintCallback = std::function<void(Canvas*, QPainter*)>;
 	Canvas(
 		Model* model,
-		EditorTabWidget* document,
+		EditTools* document,
 		DocumentManager* documents,
 		const ColorTable& colorTable,
 		QWidget* parent = nullptr);
@@ -48,7 +50,7 @@
 	DrawState* drawState = nullptr;
 public Q_SLOTS:
 	void handleSelectionChange(const QSet<ModelId>& selectedIds, const QSet<ModelId>& deselectedIds);
-	void rebuildVertices(EditorTabWidget *document);
+	void rebuildVertices(VertexMap* vertexMap);
 	void setGridMatrix(const glm::mat4 &newMatrix);
 protected:
 	void mouseMoveEvent(QMouseEvent* event) override;
@@ -74,7 +76,7 @@
 	bool isDark = true;
 	QSet<ModelId> selection;
 	OverpaintCallback overpaintCallback = nullptr;
-	EditorTabWidget* document;
+	EditTools* document;
 };
 
 void adjustGridToView(Canvas* canvas);
--- a/src/widgets/colorindexinput.cpp	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/widgets/colorindexinput.cpp	Sun Jun 12 20:47:04 2022 +0300
@@ -3,15 +3,15 @@
 #include "colorselectdialog.h"
 #include "uiutilities.h"
 
-ColorIndexInput::ColorIndexInput(EditorTabWidget *document, ColorIndex color, QWidget *parent) :
+ColorIndexInput::ColorIndexInput(ColorTable *colorTable, ColorIndex color, QWidget *parent) :
 	QWidget{parent},
-	document{document},
+	colorTable{colorTable},
 	ui{*new Ui_ColorIndexInput}
 {
 	this->ui.setupUi(this);
 	connect(this->ui.button, &QPushButton::clicked, [this]()
 	{
-		ColorSelectDialog dialog{this->document->colorTable, this->document};
+		ColorSelectDialog dialog{*this->colorTable, this};
 		const int result = dialog.exec();
 		if (result == QDialog::Accepted)
 		{
@@ -20,8 +20,9 @@
 	});
 	connect(this->ui.index, qOverload<int>(&QSpinBox::valueChanged), [this](int value)
 	{
-		this->ui.button->setText(colorDisplayName({value}, this->document->colorTable).value_or("???"));
-		const opt<QColor> face = colorFace({value}, this->document->colorTable);
+		const opt<QString> displayName = colorDisplayName({value}, *this->colorTable);
+		const opt<QColor> face = colorFace({value}, *this->colorTable);
+		this->ui.button->setText(displayName.value_or(tr("???")));
 		if (face.has_value()) {
 			uiutilities::colorizeWidget(this->ui.button, *face);
 		}
--- a/src/widgets/colorindexinput.h	Sat Jun 11 15:20:24 2022 +0300
+++ b/src/widgets/colorindexinput.h	Sun Jun 12 20:47:04 2022 +0300
@@ -5,13 +5,13 @@
 {
 	Q_OBJECT
 public:
-	ColorIndexInput(EditorTabWidget *document, ColorIndex color = MAIN_COLOR, QWidget *parent = nullptr);
+	ColorIndexInput(ColorTable *colorTable, ColorIndex color = MAIN_COLOR, QWidget *parent = nullptr);
 	~ColorIndexInput();
 	ldraw::Color selectedColor() const;
 	void setSelectedColor(ldraw::Color color);
 Q_SIGNALS:
 	void colorChanged(ldraw::Color color);
 private:
-	EditorTabWidget* const document;
+	ColorTable* const colorTable;
 	class Ui_ColorIndexInput& ui;
 };

mercurial