19 #include <QMouseEvent> |
19 #include <QMouseEvent> |
20 #include <QMessageBox> |
20 #include <QMessageBox> |
21 #include "document.h" |
21 #include "document.h" |
22 #include "ui_document.h" |
22 #include "ui_document.h" |
23 #include "model.h" |
23 #include "model.h" |
24 #include "modeleditor.h" |
|
25 #include "ui/objecteditor.h" |
24 #include "ui/objecteditor.h" |
26 #include "linetypes/edge.h" |
25 |
27 #include "linetypes/triangle.h" |
26 EditorTabWidget::EditorTabWidget( |
28 #include "linetypes/quadrilateral.h" |
|
29 |
|
30 Document::Document( |
|
31 Model* model, |
27 Model* model, |
32 DocumentManager* documents, |
28 DocumentManager* documents, |
33 const ldraw::ColorTable& colorTable, |
29 const ldraw::ColorTable& colorTable, |
34 QWidget* parent) : |
30 QWidget* parent) : |
35 QWidget{parent}, |
31 QWidget{parent}, |
37 canvas{new Canvas{model, this, documents, colorTable, this}}, |
33 canvas{new Canvas{model, this, documents, colorTable, this}}, |
38 model{model}, |
34 model{model}, |
39 documents{documents}, |
35 documents{documents}, |
40 vertexMap{model}, |
36 vertexMap{model}, |
41 ui{*new Ui_Document}, |
37 ui{*new Ui_Document}, |
42 toolsBar{new QToolBar{this}}, |
38 toolsBar{new QToolBar{this}} |
43 objectEditor{new ObjectEditor{this}} |
|
44 { |
39 { |
45 this->ui.setupUi(this); |
40 this->ui.setupUi(this); |
46 const int listWidth = static_cast<int>(this->width() / 3); |
41 const int listWidth = static_cast<int>(this->width() / 3); |
47 this->ui.viewportListSplitter->setSizes({listWidth * 2, listWidth}); |
42 this->ui.viewportListSplitter->setSizes({listWidth * 2, listWidth}); |
48 this->ui.toolsBarContainer->setLayout(new QVBoxLayout{this->ui.toolsBarContainer}); |
43 this->ui.toolsBarContainer->setLayout(new QVBoxLayout{this->ui.toolsBarContainer}); |
50 this->ui.listView->setModel(model); |
45 this->ui.listView->setModel(model); |
51 this->ui.viewportFrame->setLayout(new QVBoxLayout{this->ui.listView}); |
46 this->ui.viewportFrame->setLayout(new QVBoxLayout{this->ui.listView}); |
52 this->ui.viewportFrame->layout()->addWidget(this->canvas); |
47 this->ui.viewportFrame->layout()->addWidget(this->canvas); |
53 this->toolsBar->setOrientation(Qt::Vertical); |
48 this->toolsBar->setOrientation(Qt::Vertical); |
54 this->setMouseTracking(true); |
49 this->setMouseTracking(true); |
55 connect(this->ui.viewportListSplitter, &QSplitter::splitterMoved, this, &Document::splitterChanged); |
50 connect(this->ui.viewportListSplitter, &QSplitter::splitterMoved, this, &EditorTabWidget::splitterChanged); |
56 connect(this->canvas, &Canvas::mouseClick, this, &Document::canvasMouseClick); |
51 connect(this->canvas, &Canvas::mouseClick, this, &EditorTabWidget::canvasMouseClick); |
57 connect(this->canvas, &Canvas::mouseMove, this, &Document::canvasMouseMove); |
52 connect(this->canvas, &Canvas::mouseMove, this, &EditorTabWidget::canvasMouseMove); |
58 connect(this->canvas, &Canvas::newStatusText, this, &Document::newStatusText); |
53 connect(this->canvas, &Canvas::newStatusText, this, &EditorTabWidget::newStatusText); |
59 connect(this->ui.listView->selectionModel(), &QItemSelectionModel::selectionChanged, |
54 connect(this->ui.listView->selectionModel(), &QItemSelectionModel::selectionChanged, |
60 [&](const QItemSelection& selected, const QItemSelection& deselected) |
55 [&](const QItemSelection& selected, const QItemSelection& deselected) |
61 { |
56 { |
62 auto resolveIndex = [this](const QModelIndex& index){ return (*this->model)[index.row()]->id; }; |
57 auto resolveIndex = [this](const QModelIndex& index){ return this->model->idAt(index.row()); }; |
63 auto resolve = [resolveIndex](const QItemSelection& selection) |
58 auto resolve = [resolveIndex](const QItemSelection& selection) |
64 { |
59 { |
65 return fn::map<QSet<ldraw::id_t>>(selection.indexes(), resolveIndex); |
60 return fn::map<QSet<ModelId>>(selection.indexes(), resolveIndex); |
66 }; |
61 }; |
67 this->canvas->handleSelectionChange(resolve(selected), resolve(deselected)); |
62 this->canvas->handleSelectionChange(resolve(selected), resolve(deselected)); |
68 }); |
63 }); |
69 connect(this->model, &Model::dataChanged, this->canvas, qOverload<>(&Canvas::update)); |
64 connect(this->model, &Model::dataChanged, this->canvas, qOverload<>(&Canvas::update)); |
70 connect(&this->vertexMap, &VertexMap::verticesChanged, [&]() |
65 connect(&this->vertexMap, &VertexMap::verticesChanged, [&]() |
73 }); |
68 }); |
74 this->canvas->drawState = &this->drawState; |
69 this->canvas->drawState = &this->drawState; |
75 this->initializeTools(); |
70 this->initializeTools(); |
76 } |
71 } |
77 |
72 |
78 Document::~Document() |
73 EditorTabWidget::~EditorTabWidget() |
79 { |
74 { |
80 delete &this->ui; |
75 delete &this->ui; |
81 } |
76 } |
82 |
77 |
83 QByteArray Document::saveSplitterState() const |
78 QByteArray EditorTabWidget::saveSplitterState() const |
84 { |
79 { |
85 return this->ui.viewportListSplitter->saveState(); |
80 return this->ui.viewportListSplitter->saveState(); |
86 } |
81 } |
87 |
82 |
88 void Document::restoreSplitterState(const QByteArray& state) |
83 void EditorTabWidget::restoreSplitterState(const QByteArray& state) |
89 { |
84 { |
90 this->ui.viewportListSplitter->restoreState(state); |
85 this->ui.viewportListSplitter->restoreState(state); |
91 } |
86 } |
92 |
87 |
93 std::unique_ptr<ModelEditor> Document::editModel() |
88 void EditorTabWidget::applyToVertices(VertexMap::ApplyFunction fn) const |
94 { |
|
95 std::unique_ptr<ModelEditor> editorPointer = std::make_unique<ModelEditor>(*this->model); |
|
96 connect(editorPointer.get(), &ModelEditor::objectModified, [&](int position){ |
|
97 this->model->emitDataChangedSignal(position); |
|
98 }); |
|
99 return editorPointer; |
|
100 } |
|
101 |
|
102 void Document::applyToVertices(VertexMap::ApplyFunction fn) const |
|
103 { |
89 { |
104 this->vertexMap.apply(fn); |
90 this->vertexMap.apply(fn); |
105 } |
91 } |
106 |
92 |
107 const char INDEX_PROPERTY[] = "_editing_mode_index"; |
93 const char INDEX_PROPERTY[] = "_editing_mode_index"; |
108 |
94 |
109 void Document::initializeTools() |
95 void EditorTabWidget::initializeTools() |
110 { |
96 { |
111 const struct |
97 const struct |
112 { |
98 { |
113 QString name, tooltip; |
99 QString name, tooltip; |
114 QPixmap icon; |
100 QPixmap icon; |
116 } editingModesInfo[] = { |
102 } editingModesInfo[] = { |
117 { |
103 { |
118 .name = tr("Select"), |
104 .name = tr("Select"), |
119 .tooltip = tr("Select elements from the model."), |
105 .tooltip = tr("Select elements from the model."), |
120 .icon = {":/icons/navigate-outline.png"}, |
106 .icon = {":/icons/navigate-outline.png"}, |
121 .widget = this->objectEditor, |
107 //.widget = this->objectEditor, |
122 }, |
108 }, |
123 { |
109 { |
124 .name = tr("Draw"), |
110 .name = tr("Draw"), |
125 .tooltip = tr("Draw new elements into the model."), |
111 .tooltip = tr("Draw new elements into the model."), |
126 .icon = {":/icons/pencil-outline.png"}, |
112 .icon = {":/icons/pencil-outline.png"}, |
140 if (widget == nullptr) { |
126 if (widget == nullptr) { |
141 widget = new QWidget{this}; |
127 widget = new QWidget{this}; |
142 } |
128 } |
143 this->ui.toolWidgetStack->addWidget(widget); |
129 this->ui.toolWidgetStack->addWidget(widget); |
144 this->toolActions.push_back(action); |
130 this->toolActions.push_back(action); |
145 connect(action, &QAction::triggered, this, &Document::editingModeTriggered); |
131 connect(action, &QAction::triggered, this, &EditorTabWidget::editingModeTriggered); |
146 } |
132 } |
147 this->ui.listView->selectAll(); |
133 this->ui.listView->selectAll(); |
148 } |
134 } |
149 |
135 |
150 void Document::editingModeTriggered() |
136 void EditorTabWidget::editingModeTriggered() |
151 { |
137 { |
152 QAction* triggeredAction = qobject_cast<QAction*>(this->sender()); |
138 QAction* triggeredAction = qobject_cast<QAction*>(this->sender()); |
153 if (triggeredAction != nullptr) |
139 if (triggeredAction != nullptr) |
154 { |
140 { |
155 const int index = triggeredAction->property(INDEX_PROPERTY).toInt(); |
141 const int index = triggeredAction->property(INDEX_PROPERTY).toInt(); |
184 bool isCloseToExistingPoints(const std::vector<glm::vec3>& points, const glm::vec3 &pos) |
170 bool isCloseToExistingPoints(const std::vector<glm::vec3>& points, const glm::vec3 &pos) |
185 { |
171 { |
186 return any(points, std::bind(geom::isclose, std::placeholders::_1, pos)); |
172 return any(points, std::bind(geom::isclose, std::placeholders::_1, pos)); |
187 } |
173 } |
188 |
174 |
189 void Document::canvasMouseClick(QMouseEvent *event) |
175 void EditorTabWidget::canvasMouseClick(QMouseEvent *event) |
190 { |
176 { |
191 switch(this->drawState.mode) |
177 switch(this->drawState.mode) |
192 { |
178 { |
193 case SelectMode: |
179 case SelectMode: |
194 if (event->button() == Qt::LeftButton) |
180 if (event->button() == Qt::LeftButton) |
195 { |
181 { |
196 const ldraw::id_t highlighted = this->canvas->getHighlightedObject(); |
182 const ModelId highlighted = this->canvas->getHighlightedObject(); |
197 QSet<ldraw::id_t> selected; |
183 QSet<ModelId> selected; |
198 if (highlighted != ldraw::NULL_ID) { |
184 if (highlighted != ModelId{0}) { |
199 selected.insert(highlighted); |
185 selected.insert(highlighted); |
200 } |
186 } |
201 this->select(selected); |
187 this->select(selected); |
202 event->accept(); |
188 event->accept(); |
203 } |
189 } |
245 event->accept(); |
231 event->accept(); |
246 break; |
232 break; |
247 } |
233 } |
248 } |
234 } |
249 |
235 |
250 void Document::select(const QSet<ldraw::id_t> &selected) |
236 void EditorTabWidget::select(const QSet<ModelId> &selected) |
251 { |
237 { |
252 QItemSelectionModel* selectionModel = this->ui.listView->selectionModel(); |
238 QItemSelectionModel* selectionModel = this->ui.listView->selectionModel(); |
253 QItemSelection itemSelection; |
239 QItemSelection itemSelection; |
254 for (const ldraw::id_t id : selected) |
240 for (const ModelId id : selected) |
255 { |
241 { |
256 QModelIndex index = this->model->find(id); |
242 const std::optional<int> row = this->model->find(id); |
257 if (index != QModelIndex{}) |
243 if (row.has_value()) |
258 { |
244 { |
259 itemSelection.select(index, index); |
245 const QModelIndex qindex = this->model->index(*row); |
|
246 itemSelection.select(qindex, qindex); |
260 } |
247 } |
261 } |
248 } |
262 selectionModel->select(itemSelection, QItemSelectionModel::ClearAndSelect); |
249 selectionModel->select(itemSelection, QItemSelectionModel::ClearAndSelect); |
263 } |
250 } |
264 |
251 |
265 const Model &Document::getModel() const |
252 const QSet<ModelId> EditorTabWidget::selectedObjects() const |
266 { |
|
267 return *this->model; |
|
268 } |
|
269 |
|
270 const QSet<ldraw::id_t> Document::selectedObjects() const |
|
271 { |
253 { |
272 return this->canvas->selectedObjects(); |
254 return this->canvas->selectedObjects(); |
273 } |
255 } |
274 |
256 |
275 void Document::closeShape() |
257 void EditorTabWidget::closeShape() |
276 { |
258 { |
277 if (this->drawState.polygon.size() >= 2 and this->drawState.polygon.size() <= 4) |
259 if (this->drawState.polygon.size() >= 2 and this->drawState.polygon.size() <= 4) |
278 { |
260 { |
279 std::unique_ptr<ModelEditor> modelEditor = this->editModel(); |
|
280 switch (this->drawState.polygon.size()) |
261 switch (this->drawState.polygon.size()) |
281 { |
262 { |
282 case 2: |
263 case 2: |
283 modelEditor->append<ldraw::Edge>( |
264 this->model->append(Colored<LineSegment>{ |
284 vectorToArray<2>(this->drawState.polygon), |
265 LineSegment{ |
285 ldraw::EDGE_COLOR); |
266 .p1 = this->drawState.polygon[0], |
|
267 .p2 = this->drawState.polygon[1], |
|
268 }, |
|
269 ldraw::EDGE_COLOR}); |
286 break; |
270 break; |
287 case 3: |
271 case 3: |
288 modelEditor->append<ldraw::Triangle>( |
272 this->model->append(Colored<Triangle>{ |
289 vectorToArray<3>(this->drawState.polygon), |
273 Triangle{ |
290 ldraw::MAIN_COLOR); |
274 .p1 = this->drawState.polygon[0], |
|
275 .p2 = this->drawState.polygon[1], |
|
276 .p3 = this->drawState.polygon[2], |
|
277 }, |
|
278 ldraw::MAIN_COLOR}); |
291 break; |
279 break; |
292 case 4: |
280 case 4: |
293 modelEditor->append<ldraw::Quadrilateral>( |
281 this->model->append(Colored<Quadrilateral>{ |
294 vectorToArray<4>(this->drawState.polygon), |
282 Quadrilateral{ |
295 ldraw::MAIN_COLOR); |
283 .p1 = this->drawState.polygon[0], |
|
284 .p2 = this->drawState.polygon[1], |
|
285 .p3 = this->drawState.polygon[2], |
|
286 .p4 = this->drawState.polygon[3], |
|
287 }, |
|
288 ldraw::MAIN_COLOR}); |
296 break; |
289 break; |
297 } |
290 } |
298 } |
291 } |
299 this->drawState.polygon.clear(); |
292 this->drawState.polygon.clear(); |
300 updatePreviewPolygon(&this->drawState); |
293 updatePreviewPolygon(&this->drawState); |