From 6800ac27f3969105d78ec64f880a75c6706fb4e1 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Mon, 23 Jun 2025 04:07:04 +0200 Subject: [PATCH] refactor(selection): replaced with Selection instance --- core/src/common.cpp | 16 ------- core/src/common.h | 5 --- core/src/handles.cpp | 14 ++++--- core/src/objects/meta.cpp | 2 + core/src/objects/part.h | 1 - core/src/objects/service/selection.cpp | 42 +++++++++++++++++++ core/src/objects/service/selection.h | 28 +++++++++++++ core/src/partassembly.cpp | 5 +++ core/src/partassembly.h | 6 +-- core/src/rendering/renderer.cpp | 16 ++++--- editor/mainglwidget.cpp | 44 +++++++------------ editor/mainwindow.cpp | 58 ++++++++++---------------- editor/mainwindow.h | 2 + editor/panes/explorerview.cpp | 35 ++++++++++------ editor/panes/explorerview.h | 4 ++ editor/placedocument.cpp | 26 +++++++++++- editor/placedocument.h | 6 +++ 17 files changed, 191 insertions(+), 119 deletions(-) create mode 100644 core/src/objects/service/selection.cpp create mode 100644 core/src/objects/service/selection.h diff --git a/core/src/common.cpp b/core/src/common.cpp index 1cc79f8..f825e66 100644 --- a/core/src/common.cpp +++ b/core/src/common.cpp @@ -15,24 +15,8 @@ Handles editorToolHandles; std::vector> currentSelection; -std::vector selectionUpdateListeners; std::vector propertyUpdatelisteners; -void setSelection(std::vector> newSelection, bool fromExplorer) { - for (SelectionUpdateHandler handler : selectionUpdateListeners) { - handler(currentSelection, newSelection, fromExplorer); - } - - currentSelection = newSelection; -} - -const std::vector> getSelection() { - return currentSelection; -} - -void addSelectionListener(SelectionUpdateHandler handler) { - selectionUpdateListeners.push_back(handler); -} void sendPropertyUpdatedSignal(std::shared_ptr instance, std::string property, Variant newValue) { for (PropertyUpdateHandler handler : propertyUpdatelisteners) { diff --git a/core/src/common.h b/core/src/common.h index 86d1f49..919b194 100644 --- a/core/src/common.h +++ b/core/src/common.h @@ -11,7 +11,6 @@ class Instance; // typedef std::function element, std::optional> newParent)> HierarchyUpdateHandler; typedef std::function object, std::optional> oldParent, std::optional> newParent)> HierarchyPreUpdateHandler; typedef std::function object, std::optional> oldParent, std::optional> newParent)> HierarchyPostUpdateHandler; -typedef std::function> oldSelection, std::vector> newSelection, bool fromExplorer)> SelectionUpdateHandler; typedef std::function instance, std::string property, Variant newValue)> PropertyUpdateHandler; // TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS @@ -24,9 +23,5 @@ extern std::optional hierarchyPreUpdateHandler; extern std::optional hierarchyPostUpdateHandler; extern Handles editorToolHandles; -void setSelection(std::vector> newSelection, bool fromExplorer = false); -const std::vector> getSelection(); -void addSelectionListener(SelectionUpdateHandler handler); - void sendPropertyUpdatedSignal(std::shared_ptr instance, std::string property, Variant newValue); void addPropertyUpdateListener(PropertyUpdateHandler handler); \ No newline at end of file diff --git a/core/src/handles.cpp b/core/src/handles.cpp index a35e37b..d8c480c 100644 --- a/core/src/handles.cpp +++ b/core/src/handles.cpp @@ -3,6 +3,7 @@ #include "datatypes/cframe.h" #include "datatypes/vector.h" #include "math_helper.h" +#include "objects/service/selection.h" #include "partassembly.h" #include #include @@ -27,7 +28,8 @@ static rp3d::PhysicsCommon common; static rp3d::PhysicsWorld* world = common.createPhysicsWorld(); std::shared_ptr getHandleAdornee() { - for (std::weak_ptr inst : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (std::weak_ptr inst : selection->Get()) { if (!inst.expired() && inst.lock()->IsA()) return inst.lock()->CastTo().expect(); } @@ -78,7 +80,8 @@ Vector3 handleSize(HandleFace face) { static int getAABBOfSelection(glm::vec3& pos, glm::vec3& size, glm::vec3& min, glm::vec3& max) { int count = 0; - for (std::weak_ptr inst : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (std::weak_ptr inst : selection->Get()) { if (inst.expired() || !inst.lock()->IsA()) continue; std::shared_ptr part = inst.lock()->CastTo().expect(); @@ -97,7 +100,8 @@ static int getAABBOfSelection(glm::vec3& pos, glm::vec3& size, glm::vec3& min, g } static std::shared_ptr getFirstSelectedPart() { - for (std::weak_ptr inst : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (std::weak_ptr inst : selection->Get()) { if (inst.expired() || !inst.lock()->IsA()) continue; return inst.lock()->CastTo().expect(); @@ -107,7 +111,7 @@ static std::shared_ptr getFirstSelectedPart() { } CFrame getLocalHandleCFrame(HandleFace face) { - PartAssembly assembly = PartAssembly::FromSelection(); + PartAssembly assembly = PartAssembly::FromSelection(gDataModel->GetService()); Vector3 size; if (editorToolHandles.worldMode) @@ -125,7 +129,7 @@ CFrame getLocalHandleCFrame(HandleFace face) { } CFrame getHandleCFrame(HandleFace face) { - PartAssembly assembly = PartAssembly::FromSelection(); + PartAssembly assembly = PartAssembly::FromSelection(gDataModel->GetService()); if (editorToolHandles.worldMode) { return getLocalHandleCFrame(face) + assembly.assemblyOrigin().Position(); diff --git a/core/src/objects/meta.cpp b/core/src/objects/meta.cpp index 16b3d3c..eec6931 100644 --- a/core/src/objects/meta.cpp +++ b/core/src/objects/meta.cpp @@ -11,6 +11,7 @@ #include "objects/script.h" #include "objects/service/script/scriptcontext.h" #include "objects/service/script/serverscriptservice.h" +#include "objects/service/selection.h" #include "objects/service/workspace.h" #include "objects/datamodel.h" @@ -34,4 +35,5 @@ std::map INSTANCE_MAP = { { "JointsService", &JointsService::TYPE }, { "ScriptContext", &ScriptContext::TYPE }, { "ServerScriptService", &ServerScriptService::TYPE }, + { "Selection", &Selection::TYPE }, }; \ No newline at end of file diff --git a/core/src/objects/part.h b/core/src/objects/part.h index cf1a61e..a0e1ea8 100644 --- a/core/src/objects/part.h +++ b/core/src/objects/part.h @@ -66,7 +66,6 @@ public: DEF_PROP_CATEGORY(APPEARANCE) DEF_PROP Color3 color; DEF_PROP float transparency = 0.f; - bool selected = false; DEF_PROP_CATEGORY(BEHAVIOR) DEF_PROP_(on_update=onUpdated) bool anchored = false; diff --git a/core/src/objects/service/selection.cpp b/core/src/objects/service/selection.cpp new file mode 100644 index 0000000..d88a924 --- /dev/null +++ b/core/src/objects/service/selection.cpp @@ -0,0 +1,42 @@ +#include "selection.h" +#include "objects/datamodel.h" +#include +#include + +Selection::Selection(): Service(&TYPE) { +} + +Selection::~Selection() = default; + +void Selection::InitService() { + if (initialized) return; + initialized = true; +} + +std::vector> Selection::Get() { + return selection; +} + +void Selection::Set(std::vector> newSelection) { + selection = newSelection; + SelectionChanged->Fire(); +} + +void Selection::Add(std::vector> instances) { + for (auto inst : instances) { + if (std::find(selection.begin(), selection.end(), inst) == selection.end()) { + selection.push_back(inst); + } + } + SelectionChanged->Fire(); +} + +void Selection::Remove(std::vector> instances) { + for (auto inst : instances) { + std::vector>::iterator p; + if ((p = std::find(selection.begin(), selection.end(), inst)) != selection.end()) { + selection.erase(p); + } + } + SelectionChanged->Fire(); +} \ No newline at end of file diff --git a/core/src/objects/service/selection.h b/core/src/objects/service/selection.h new file mode 100644 index 0000000..43c6e2b --- /dev/null +++ b/core/src/objects/service/selection.h @@ -0,0 +1,28 @@ +#pragma once + +#include "datatypes/signal.h" +#include "objects/annotation.h" +#include "objects/base/service.h" +#include +#include + +class DEF_INST_SERVICE Selection : public Service { + AUTOGEN_PREAMBLE +private: + std::vector> selection; + protected: + void InitService() override; + bool initialized = false; +public: + Selection(); + ~Selection(); + static inline std::shared_ptr Create() { return std::make_shared(); }; + + std::vector> Get(); + void Set(std::vector> newSelection); + + void Add(std::vector> instances); + void Remove(std::vector> instances); + + SignalSource SelectionChanged; +}; \ No newline at end of file diff --git a/core/src/partassembly.cpp b/core/src/partassembly.cpp index 04e440e..5fe7114 100644 --- a/core/src/partassembly.cpp +++ b/core/src/partassembly.cpp @@ -6,6 +6,7 @@ #include "math_helper.h" #include "objects/base/instance.h" #include "objects/part.h" +#include "objects/service/selection.h" #include #include #include @@ -54,6 +55,10 @@ PartAssembly PartAssembly::FromSelection(std::vector> return PartAssembly(selection, editorToolHandles.worldMode); } +PartAssembly PartAssembly::FromSelection(std::shared_ptr selection) { + return FromSelection(selection->Get()); +} + void PartAssembly::SetCollisionsEnabled(bool enabled) { for (auto part : parts) { part->rigidBody->getCollider(0)->setIsWorldQueryCollider(enabled); diff --git a/core/src/partassembly.h b/core/src/partassembly.h index 8071a64..81ca398 100644 --- a/core/src/partassembly.h +++ b/core/src/partassembly.h @@ -6,8 +6,7 @@ class Part; class Instance; - -const std::vector> getSelection(); +class Selection; class PartAssembly { CFrame _assemblyOrigin; @@ -18,7 +17,8 @@ class PartAssembly { public: PartAssembly(std::vector>, bool worldMode = false); - static PartAssembly FromSelection(std::vector> selection = getSelection()); + static PartAssembly FromSelection(std::vector> selection); + static PartAssembly FromSelection(std::shared_ptr selection); inline CFrame assemblyOrigin() { return _assemblyOrigin; }; inline Vector3 bounds() { return _bounds; }; diff --git a/core/src/rendering/renderer.cpp b/core/src/rendering/renderer.cpp index f5d73b2..8854453 100644 --- a/core/src/rendering/renderer.cpp +++ b/core/src/rendering/renderer.cpp @@ -19,6 +19,7 @@ #include "datatypes/vector.h" #include "handles.h" #include "math_helper.h" +#include "objects/service/selection.h" #include "partassembly.h" #include "rendering/torus.h" #include "shader.h" @@ -270,7 +271,7 @@ static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, void renderHandles() { if (!editorToolHandles.active) return; - auto assembly = PartAssembly::FromSelection(); + auto assembly = PartAssembly::FromSelection(gDataModel->GetService()); if (assembly.size() == Vector3::ZERO) return; glDepthMask(GL_TRUE); @@ -448,15 +449,12 @@ void renderOutlines() { glm::vec3 min, max; bool first = true; - int count = 0; - for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) { - std::shared_ptr inst = *it; + std::shared_ptr selection = gDataModel->GetService(); + for (auto inst : selection->Get()) { if (inst->GetClass() != &Part::TYPE) continue; std::shared_ptr part = std::dynamic_pointer_cast(inst); - if (!part->selected) continue; - count++; if (first) min = part->position(), max = part->position(); first = false; @@ -475,7 +473,7 @@ void renderOutlines() { } // Render AABB of selected parts - PartAssembly selectionAssembly = PartAssembly::FromSelection(); + PartAssembly selectionAssembly = PartAssembly::FromSelection(gDataModel->GetService()); if (selectionAssembly.size() == Vector3()) return; glm::vec3 outlineSize = selectionAssembly.bounds(); glm::vec3 outlinePos = selectionAssembly.assemblyOrigin().Position(); @@ -498,7 +496,7 @@ void renderSelectionAssembly() { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - PartAssembly selectionAssembly = PartAssembly::FromSelection(); + PartAssembly selectionAssembly = PartAssembly::FromSelection(gDataModel->GetService()); // Use shader outlineShader->use(); @@ -555,7 +553,7 @@ void renderRotationArcs() { // Pass in the camera position handleShader->set("viewPos", camera.cameraPos); - PartAssembly assembly = PartAssembly::FromSelection(); + PartAssembly assembly = PartAssembly::FromSelection(gDataModel->GetService()); for (HandleFace face : HandleFace::Faces) { if (glm::any(glm::lessThan(face.normal, glm::vec3(0)))) continue; diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index f2b279c..210154f 100755 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -15,6 +15,7 @@ #include "math_helper.h" #include "objects/base/instance.h" #include "objects/pvinstance.h" +#include "objects/service/selection.h" #include "partassembly.h" #include "physics/util.h" #include "rendering/renderer.h" @@ -217,7 +218,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) { absDiff = handleCFrame.Rotation() * Vector3(0, 0, diff); } - PartAssembly selectionAssembly = PartAssembly::FromSelection(); + PartAssembly selectionAssembly = PartAssembly::FromSelection(gDataModel->GetService()); if (editorToolHandles.handlesType == MoveHandles) { selectionAssembly.TransformBy(CFrame() + absDiff); @@ -374,7 +375,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { auto handle = raycastHandle(pointDir); if (handle.has_value()) { startPoint = glm::vec2(evt->pos().x(), evt->pos().y()); - initialAssembly = PartAssembly::FromSelection(); + initialAssembly = PartAssembly::FromSelection(gDataModel->GetService()); initialFrame = initialAssembly.assemblyOrigin(); isMouseDragging = true; draggingHandle = handle; @@ -383,10 +384,11 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { } // raycast part + std::shared_ptr selection = gDataModel->GetService(); std::optional rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000); - if (!rayHit || !partFromBody(rayHit->body)) { setSelection({}); return; } + if (!rayHit || !partFromBody(rayHit->body)) { selection->Set({}); return; } std::shared_ptr part = partFromBody(rayHit->body); - if (part->locked) { setSelection({}); return; } + if (part->locked) { selection->Set({}); return; } std::shared_ptr selObject = part; @@ -429,19 +431,14 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { isMouseDragging = true; draggingObject = part; if (evt->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier)) { - std::vector> currentSelection = getSelection(); - for (size_t i = 0; i < currentSelection.size(); i++) { - std::shared_ptr inst = currentSelection[i]; - if (inst == selObject) { - currentSelection.erase(currentSelection.begin() + i); - goto skipAddPart; - } - } - currentSelection.push_back(selObject); - skipAddPart: - setSelection(currentSelection); - } else - setSelection({ selObject }); + auto sel = selection->Get(); + if (std::find(sel.begin(), sel.end(), selObject) == sel.end()) + selection->Add({ selObject }); + else + selection->Remove({ selObject }); + } else { + selection->Set({ selObject }); + } // Disable bit so that we can ignore the part while raycasting // part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10); @@ -509,19 +506,6 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) { gWorkspace()->SyncPartPhysics(lastPart); lastPart->name = "Part" + std::to_string(partId++); } - - if (evt->key() == Qt::Key_U) - Logger::info("info message"); - if (evt->key() == Qt::Key_I) - Logger::warning("warning message"); - if (evt->key() == Qt::Key_O) - Logger::error("error message"); - - if (evt->key() == Qt::Key_C && getSelection().size() > 0) - getSelection()[0]->Clone().value()->SetParent(gWorkspace()); - - if (evt->key() == Qt::Key_H && getSelection().size() > 0) - Logger::infof("Object at: 0x%x\n", getSelection()[0].get()); } void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) { diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index 1edf615..30a3a6a 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -5,6 +5,7 @@ #include "logger.h" #include "objects/datamodel.h" #include "objects/model.h" +#include "objects/service/selection.h" #include "placedocument.h" #include "script/scriptdocument.h" #include @@ -97,28 +98,6 @@ MainWindow::MainWindow(QWidget *parent) connectActionHandlers(); - // Update properties - addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { - if (newSelection.size() == 0) return; - if (newSelection.size() > 1) - ui->propertiesView->setSelected(std::nullopt); - ui->propertiesView->setSelected(newSelection[0]); - }); - - addSelectionListener([&](auto oldSelection, auto newSelection, bool __) { - for (std::weak_ptr inst : oldSelection) { - if (inst.expired() || inst.lock()->GetClass() != &Part::TYPE) continue; - std::shared_ptr part = std::dynamic_pointer_cast(inst.lock()); - part->selected = false; - } - - for (std::weak_ptr inst : newSelection) { - if (inst.expired() || inst.lock()->GetClass() != &Part::TYPE) continue; - std::shared_ptr part = std::dynamic_pointer_cast(inst.lock()); - part->selected = true; - } - }); - // ui->explorerView->Init(ui); placeDocument = new PlaceDocument(this); placeDocument->setAttribute(Qt::WA_DeleteOnClose, true); @@ -299,16 +278,18 @@ void MainWindow::connectActionHandlers() { }); connect(ui->actionDelete, &QAction::triggered, this, [&]() { - for (std::weak_ptr inst : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (std::weak_ptr inst : selection->Get()) { if (inst.expired()) continue; inst.lock()->SetParent(std::nullopt); } - setSelection({}); + selection->Set({}); }); connect(ui->actionCopy, &QAction::triggered, this, [&]() { pugi::xml_document rootDoc; - for (std::weak_ptr inst : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (std::weak_ptr inst : selection->Get()) { if (inst.expired()) continue; inst.lock()->Serialize(rootDoc); } @@ -322,11 +303,13 @@ void MainWindow::connectActionHandlers() { }); connect(ui->actionCut, &QAction::triggered, this, [&]() { pugi::xml_document rootDoc; - for (std::weak_ptr inst : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (std::weak_ptr inst : selection->Get()) { if (inst.expired()) continue; inst.lock()->Serialize(rootDoc); inst.lock()->SetParent(std::nullopt); } + selection->Set({}); std::ostringstream encoded; rootDoc.save(encoded); @@ -353,9 +336,10 @@ void MainWindow::connectActionHandlers() { }); connect(ui->actionPasteInto, &QAction::triggered, this, [&]() { - if (getSelection().size() != 1) return; + std::shared_ptr selection = gDataModel->GetService(); + if (selection->Get().size() != 1) return; - std::shared_ptr selectedParent = getSelection()[0]; + std::shared_ptr selectedParent = selection->Get()[0]; const QMimeData* mimeData = QApplication::clipboard()->mimeData(); if (!mimeData || !mimeData->hasFormat("application/xml")) return; @@ -376,7 +360,8 @@ void MainWindow::connectActionHandlers() { auto model = Model::New(); std::shared_ptr firstParent; - for (auto object : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (auto object : selection->Get()) { if (firstParent == nullptr && object->GetParent().has_value()) firstParent = object->GetParent().value(); object->SetParent(model); } @@ -389,14 +374,15 @@ void MainWindow::connectActionHandlers() { if (firstParent == nullptr) firstParent = gWorkspace(); model->SetParent(firstParent); - setSelection({ model }); + selection->Set({ model }); playSound("./assets/excluded/electronicpingshort.wav"); }); connect(ui->actionUngroupObjects, &QAction::triggered, this, [&]() { std::vector> newSelection; - for (auto model : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (auto model : selection->Get()) { // Not a model, skip if (!model->IsA()) { newSelection.push_back(model); continue; } @@ -408,7 +394,7 @@ void MainWindow::connectActionHandlers() { model->Destroy(); } - setSelection(newSelection); + selection->Set(newSelection); playSound("./assets/excluded/electronicpingshort.wav"); }); @@ -421,7 +407,8 @@ void MainWindow::connectActionHandlers() { pugi::xml_document modelDoc; pugi::xml_node modelRoot = modelDoc.append_child("openblocks"); - for (std::weak_ptr inst : getSelection()) { + std::shared_ptr selection = gDataModel->GetService(); + for (std::weak_ptr inst : selection->Get()) { if (inst.expired()) continue; inst.lock()->Serialize(modelRoot); } @@ -430,8 +417,9 @@ void MainWindow::connectActionHandlers() { }); connect(ui->actionInsertModel, &QAction::triggered, this, [&]() { - if (getSelection().size() != 1) return; - std::shared_ptr selectedParent = getSelection()[0]; + std::shared_ptr selection = gDataModel->GetService(); + if (selection->Get().size() != 1) return; + std::shared_ptr selectedParent = selection->Get()[0]; std::optional path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptOpen); if (!path) return; diff --git a/editor/mainwindow.h b/editor/mainwindow.h index 5ee1e2d..bef1914 100644 --- a/editor/mainwindow.h +++ b/editor/mainwindow.h @@ -60,6 +60,8 @@ public: void openFile(std::string path); Ui::MainWindow *ui; + + friend PlaceDocument; private: PlaceDocument* placeDocument; std::map, ScriptDocument*> scriptDocuments; diff --git a/editor/panes/explorerview.cpp b/editor/panes/explorerview.cpp index c40184c..70ceb55 100644 --- a/editor/panes/explorerview.cpp +++ b/editor/panes/explorerview.cpp @@ -5,6 +5,7 @@ #include "objects/base/instance.h" #include "objects/meta.h" #include "objects/script.h" +#include "objects/service/selection.h" #include #include #include @@ -37,6 +38,10 @@ ExplorerView::ExplorerView(QWidget* parent): }); connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) { + // Simple, good old debounce. + if (handlingSelectionUpdate) + return; + std::vector> selectedInstances; selectedInstances.reserve(selectedIndexes().count()); // This doesn't reserve everything, but should enhance things anyway @@ -44,20 +49,23 @@ ExplorerView::ExplorerView(QWidget* parent): selectedInstances.push_back(reinterpret_cast(index.internalPointer())->shared_from_this()); } - ::setSelection(selectedInstances, true); + std::shared_ptr selection = gDataModel->GetService(); + selection->Set(selectedInstances); }); - addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { - // It's from us, ignore it. - if (fromExplorer) return; +} - this->clearSelection(); - for (std::weak_ptr inst : newSelection) { - if (inst.expired()) continue; - QModelIndex index = this->model.ObjectToIndex(inst.lock()); - this->selectionModel()->select(index, QItemSelectionModel::SelectionFlag::Select); - } - }); +void ExplorerView::setSelectedObjects(std::vector> selection) { + handlingSelectionUpdate = true; + + this->clearSelection(); + for (std::weak_ptr inst : selection) { + if (inst.expired()) continue; + QModelIndex index = this->model.ObjectToIndex(inst.lock()); + this->selectionModel()->select(index, QItemSelectionModel::SelectionFlag::Select); + } + + handlingSelectionUpdate = false; } ExplorerView::~ExplorerView() { @@ -103,8 +111,9 @@ void ExplorerView::buildContextMenu() { QAction* instAction = new QAction(model.iconOf(type), QString::fromStdString(type->className)); insertObjectMenu->addAction(instAction); connect(instAction, &QAction::triggered, this, [&]() { - if (getSelection().size() == 0) return; - std::shared_ptr instParent = getSelection()[0]; + std::shared_ptr selection = gDataModel->GetService(); + if (selection->Get().size() == 0) return; + std::shared_ptr instParent = selection->Get()[0]; std::shared_ptr newInst = type->constructor(); newInst->SetParent(instParent); }); diff --git a/editor/panes/explorerview.h b/editor/panes/explorerview.h index e181840..b8b934b 100644 --- a/editor/panes/explorerview.h +++ b/editor/panes/explorerview.h @@ -1,12 +1,14 @@ #pragma once #include "panes/explorermodel.h" +#include #include #include class Ui_MainWindow; class ExplorerView : public QTreeView { + bool handlingSelectionUpdate = false; public: ExplorerView(QWidget* parent = nullptr); ~ExplorerView() override; @@ -17,6 +19,8 @@ public: void buildContextMenu(); void updateRoot(std::shared_ptr newRoot); + + void setSelectedObjects(std::vector> selection); private: ExplorerModel model; QMenu contextMenu; diff --git a/editor/placedocument.cpp b/editor/placedocument.cpp index fc7b53e..17adae6 100644 --- a/editor/placedocument.cpp +++ b/editor/placedocument.cpp @@ -1,5 +1,6 @@ #include "placedocument.h" #include "common.h" +#include "datatypes/variant.h" #include "mainglwidget.h" #include "mainwindow.h" #include "objects/joint/snap.h" @@ -15,6 +16,8 @@ #include #include #include +#include "../ui_mainwindow.h" +#include "objects/service/selection.h" PlaceDocument::PlaceDocument(QWidget* parent): QMdiSubWindow(parent) { @@ -24,6 +27,7 @@ PlaceDocument::PlaceDocument(QWidget* parent): setWindowTitle("Place"); _runState = RUN_STOPPED; + updateSelectionListeners(gDataModel->GetService()); } PlaceDocument::~PlaceDocument() { @@ -41,7 +45,7 @@ void PlaceDocument::setRunState(RunState newState) { std::shared_ptr newModel = editModeDataModel->CloneModel(); gDataModel = newModel; gDataModel->Init(true); - setSelection({}); + updateSelectionListeners(gDataModel->GetService()); } else if (newState == RUN_PAUSED && _runState == RUN_RUNNING) { _runState = RUN_PAUSED; } else if (newState == RUN_STOPPED) { @@ -49,10 +53,28 @@ void PlaceDocument::setRunState(RunState newState) { // TODO: GC: Check to make sure gDataModel gets properly garbage collected prior to this gDataModel = editModeDataModel; - setSelection({}); + updateSelectionListeners(gDataModel->GetService()); } } +void PlaceDocument::updateSelectionListeners(std::shared_ptr selection) { + MainWindow* mainWnd = dynamic_cast(window()); + + if (!selectionConnection.expired()) + selectionConnection.lock()->Disconnect(); + + selectionConnection = selection->SelectionChanged->Connect([selection, mainWnd](std::vector _){ + // Update properties + if (selection->Get().size() != 1) + mainWnd->ui->propertiesView->setSelected(std::nullopt); + else + mainWnd->ui->propertiesView->setSelected(selection->Get()[0]); + + // Update explorer + mainWnd->ui->explorerView->setSelectedObjects(selection->Get()); + }); +} + void PlaceDocument::closeEvent(QCloseEvent *closeEvent) { // Placeholder closeEvent->ignore(); diff --git a/editor/placedocument.h b/editor/placedocument.h index 5894f02..6509be5 100644 --- a/editor/placedocument.h +++ b/editor/placedocument.h @@ -1,10 +1,13 @@ #pragma once +#include "datatypes/signal.h" #include "mainglwidget.h" #include #include #include +class Selection; + enum RunState { RUN_STOPPED, RUN_RUNNING, @@ -15,7 +18,10 @@ class PlaceDocument : public QMdiSubWindow { QBasicTimer timer; RunState _runState; + std::weak_ptr selectionConnection; + void timerEvent(QTimerEvent*) override; + void updateSelectionListeners(std::shared_ptr); public: MainGLWidget* placeWidget; PlaceDocument(QWidget* parent = nullptr);