diff --git a/core/src/objects/base/instance.cpp b/core/src/objects/base/instance.cpp index 8822843..56f9e6a 100644 --- a/core/src/objects/base/instance.cpp +++ b/core/src/objects/base/instance.cpp @@ -56,6 +56,16 @@ bool operator ==(std::optional> a, std::optional +bool operator ==(std::weak_ptr a, std::weak_ptr b) { + return a.expired() && b.expired() || (!a.expired() && !b.expired() && a.lock() == b.lock()); +} + +template +std::weak_ptr optional_to_weak(std::optional> a) { + return a ? a.value() : std::weak_ptr(); +} + // TODO: Test this bool Instance::ancestryContinuityCheck(std::optional> newParent) { for (std::optional> currentParent = newParent; currentParent.has_value(); currentParent = currentParent.value()->GetParent()) { @@ -72,15 +82,15 @@ bool Instance::SetParent(std::optional> newParent) { auto lastParent = GetParent(); if (hierarchyPreUpdateHandler.has_value()) hierarchyPreUpdateHandler.value()(this->shared_from_this(), lastParent, newParent); // If we currently have a parent, remove ourselves from it before adding ourselves to the new one - if (this->parent.has_value() && !this->parent.value().expired()) { - auto oldParent = this->parent.value().lock(); + if (!this->parent.expired()) { + auto oldParent = this->parent.lock(); oldParent->children.erase(std::find(oldParent->children.begin(), oldParent->children.end(), this->shared_from_this())); } // Add ourselves to the new parent if (newParent.has_value()) { newParent.value()->children.push_back(this->shared_from_this()); } - this->parent = newParent; + this->parent = optional_to_weak(newParent); // TODO: Add code for sending signals for parent updates // TODO: Yeahhh maybe this isn't the best way of doing this? if (hierarchyPostUpdateHandler.has_value()) hierarchyPostUpdateHandler.value()(this->shared_from_this(), lastParent, newParent); @@ -98,23 +108,23 @@ void Instance::updateAncestry(std::optional> updatedCh // Update parent data model and workspace, if applicable if (GetParent()) { - this->_dataModel = GetParent().value()->GetClass() == &DataModel::TYPE ? std::make_optional(std::dynamic_pointer_cast(GetParent().value())) : GetParent().value()->dataModel(); - this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::make_optional(std::dynamic_pointer_cast(GetParent().value())) : GetParent().value()->workspace(); + this->_dataModel = GetParent().value()->GetClass() == &DataModel::TYPE ? std::dynamic_pointer_cast(GetParent().value()) : GetParent().value()->_dataModel; + this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::dynamic_pointer_cast(GetParent().value()) : GetParent().value()->_workspace; } else { - this->_dataModel = std::nullopt; - this->_workspace = std::nullopt; + this->_dataModel = {}; + this->_workspace = {}; } OnAncestryChanged(updatedChild, newParent); // Old workspace used to exist, and workspaces differ - if (oldWorkspace.has_value() && !oldWorkspace->expired() && (!_workspace || _workspace->expired() || oldWorkspace->lock() != _workspace->lock())) { - OnWorkspaceRemoved((oldWorkspace.has_value() && !oldWorkspace->expired()) ? std::make_optional(oldWorkspace->lock()) : std::nullopt); + if (!oldWorkspace.expired() && oldWorkspace != _workspace) { + OnWorkspaceRemoved(!oldWorkspace.expired() ? std::make_optional(oldWorkspace.lock()) : std::nullopt); } // New workspace exists, and workspaces differ - if (_workspace.has_value() && !_workspace->expired() && (!oldWorkspace || oldWorkspace->expired() || _workspace->lock() != oldWorkspace->lock())) { - OnWorkspaceAdded((oldWorkspace.has_value() && !oldWorkspace->expired()) ? std::make_optional(oldWorkspace->lock()) : std::nullopt, _workspace->lock()); + if (!_workspace.expired() && (_workspace != oldWorkspace)) { + OnWorkspaceAdded(!oldWorkspace.expired() ? std::make_optional(oldWorkspace.lock()) : std::nullopt, _workspace.lock()); } // Update ancestry in descendants @@ -124,17 +134,16 @@ void Instance::updateAncestry(std::optional> updatedCh } std::optional> Instance::dataModel() { - return (!_dataModel || _dataModel->expired()) ? std::nullopt : std::make_optional(_dataModel.value().lock()); + return (_dataModel.expired()) ? std::nullopt : std::make_optional(_dataModel.lock()); } std::optional> Instance::workspace() { - return (!_workspace || _workspace->expired()) ? std::nullopt : std::make_optional(_workspace.value().lock()); + return (_workspace.expired()) ? std::nullopt : std::make_optional(_workspace.lock()); } std::optional> Instance::GetParent() { - if (!parent.has_value()) return std::nullopt; - if (parent.value().expired()) return std::nullopt; - return parent.value().lock(); + if (parent.expired()) return std::nullopt; + return parent.lock(); } static std::shared_ptr DUMMY_INSTANCE; diff --git a/core/src/objects/base/instance.h b/core/src/objects/base/instance.h index 21ab401..c151f13 100644 --- a/core/src/objects/base/instance.h +++ b/core/src/objects/base/instance.h @@ -45,13 +45,13 @@ class DescendantsIterator; // https://stackoverflow.com/q/56415222/16255372 class Instance : public std::enable_shared_from_this { private: - std::optional> parent; + std::weak_ptr parent; std::vector> children; std::optional> cachedMemberList; - std::optional> _dataModel; - std::optional> _workspace; + std::weak_ptr _dataModel; + std::weak_ptr _workspace; bool ancestryContinuityCheck(std::optional> newParent); void updateAncestry(std::optional> child, std::optional> newParent); diff --git a/core/src/objects/handles.cpp b/core/src/objects/handles.cpp index 5c07fb3..b98130d 100644 --- a/core/src/objects/handles.cpp +++ b/core/src/objects/handles.cpp @@ -37,9 +37,9 @@ Handles::Handles(): Instance(&TYPE) { } Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) { - if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0)); + if (adornee.expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0)); - Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe; + Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee.lock()->position() : adornee.lock()->cframe; Data::Vector3 handleNormal = face.normal; if (nixAxes) @@ -50,7 +50,7 @@ Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) { if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * handleNormal), upAxis)) > 0.9999f) upAxis = glm::vec3(0, 1, 0); - Data::Vector3 handleOffset = this->worldMode ? ((Data::Vector3::ONE * 2.f) + adornee->lock()->GetAABB() * 0.5f) : Data::Vector3(2.f + adornee->lock()->size * 0.5f); + Data::Vector3 handleOffset = this->worldMode ? ((Data::Vector3::ONE * 2.f) + adornee.lock()->GetAABB() * 0.5f) : Data::Vector3(2.f + adornee.lock()->size * 0.5f); Data::Vector3 handlePos = localFrame * (handleOffset * handleNormal); Data::CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -handleNormal, upAxis); @@ -58,17 +58,17 @@ Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) { } Data::CFrame Handles::PartCFrameFromHandlePos(HandleFace face, Data::Vector3 newPos) { - if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0)); + if (adornee.expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0)); - Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe; + Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee.lock()->position() : adornee.lock()->cframe; Data::CFrame inverseFrame = localFrame.Inverse(); - Data::Vector3 handleOffset = this->worldMode ? ((Data::Vector3::ONE * 2.f) + adornee->lock()->GetAABB() * 0.5f) : Data::Vector3(2.f + adornee->lock()->size * 0.5f); + Data::Vector3 handleOffset = this->worldMode ? ((Data::Vector3::ONE * 2.f) + adornee.lock()->GetAABB() * 0.5f) : Data::Vector3(2.f + adornee.lock()->size * 0.5f); Data::Vector3 handlePos = localFrame * (handleOffset * face.normal); // glm::vec3 localPos = inverseFrame * newPos; glm::vec3 newPartPos = newPos - localFrame.Rotation() * (handleOffset * face.normal); - return adornee->lock()->cframe.Rotation() + newPartPos; + return adornee.lock()->cframe.Rotation() + newPartPos; } std::optional Handles::RaycastHandle(rp3d::Ray ray) { diff --git a/core/src/objects/handles.h b/core/src/objects/handles.h index 86a18ba..8a915d2 100644 --- a/core/src/objects/handles.h +++ b/core/src/objects/handles.h @@ -36,11 +36,11 @@ public: bool nixAxes = false; // XYZ -> ZXY, used with rotation bool active; - std::optional> adornee; + std::weak_ptr adornee; HandlesType handlesType; - // inline std::optional> GetAdornee() { return adornee; } - // inline void SetAdornee(std::optional> newAdornee) { this->adornee = newAdornee; updateAdornee(); }; + // inline std::weak_ptr GetAdornee() { return adornee; } + // inline void SetAdornee(std::weak_ptr newAdornee) { this->adornee = newAdornee; updateAdornee(); }; Handles(); diff --git a/core/src/objects/snap.cpp b/core/src/objects/snap.cpp index 6c90c83..803ddd7 100644 --- a/core/src/objects/snap.cpp +++ b/core/src/objects/snap.cpp @@ -22,18 +22,18 @@ Snap::~Snap() { } void Snap::OnWorkspaceAdded(std::optional> oldWorkspace, std::shared_ptr newWorkspace) { - if (!part0 || !part1 || part0->expired() || part1->expired()) return; + if (part0.expired() || part1.expired()) return; - printVec((part0->lock()->cframe * (c1.Inverse() * c0)).Rotation().ToEulerAnglesXYZ()); - printVec(part1->lock()->cframe.Rotation().ToEulerAnglesXYZ()); + printVec((part0.lock()->cframe * (c1.Inverse() * c0)).Rotation().ToEulerAnglesXYZ()); + printVec(part1.lock()->cframe.Rotation().ToEulerAnglesXYZ()); // Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it // used to be rather than specifying an anchor rotation, so whatever. - Data::CFrame newFrame = part0->lock()->cframe * (c1.Inverse() * c0); - part1->lock()->cframe = newFrame; - newWorkspace->SyncPartPhysics(part1->lock()); + Data::CFrame newFrame = part0.lock()->cframe * (c1.Inverse() * c0); + part1.lock()->cframe = newFrame; + newWorkspace->SyncPartPhysics(part1.lock()); - rp::FixedJointInfo jointInfo(part0->lock()->rigidBody, part1->lock()->rigidBody, (c0.Inverse() * c1).Position()); + rp::FixedJointInfo jointInfo(part0.lock()->rigidBody, part1.lock()->rigidBody, (c0.Inverse() * c1).Position()); this->joint = dynamic_cast(workspace().value()->physicsWorld->createJoint(jointInfo)); } diff --git a/core/src/objects/snap.h b/core/src/objects/snap.h index c8e8f17..e11d7d5 100644 --- a/core/src/objects/snap.h +++ b/core/src/objects/snap.h @@ -14,8 +14,8 @@ protected: public: const static InstanceType TYPE; - std::optional> part0; - std::optional> part1; + std::weak_ptr part0; + std::weak_ptr part1; Data::CFrame c0; Data::CFrame c1; diff --git a/core/src/rendering/renderer.cpp b/core/src/rendering/renderer.cpp index bef9e31..b778b89 100644 --- a/core/src/rendering/renderer.cpp +++ b/core/src/rendering/renderer.cpp @@ -205,7 +205,7 @@ void renderSkyBox() { } void renderHandles() { - if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return; + if (editorToolHandles->adornee.expired() || !editorToolHandles->active) return; glDepthMask(GL_TRUE); glCullFace(GL_BACK); diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index 2493284..c136ca2 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -44,7 +44,7 @@ void MainGLWidget::resizeGL(int w, int h) { glm::vec2 firstPoint; glm::vec2 secondPoint; -extern std::optional> draggingObject; +extern std::weak_ptr draggingObject; extern std::optional draggingHandle; extern Shader* shader; void MainGLWidget::paintGL() { @@ -107,19 +107,19 @@ Data::CFrame snapCFrame(Data::CFrame frame) { } bool isMouseDragging = false; -std::optional> draggingObject; +std::weak_ptr draggingObject; std::optional draggingHandle; Data::Vector3 initialHitPos; Data::Vector3 initialHitNormal; Data::CFrame initialFrame; void MainGLWidget::handleObjectDrag(QMouseEvent* evt) { - if (!isMouseDragging || !draggingObject || mainWindow()->selectedTool >= TOOL_SMOOTH) return; + if (!isMouseDragging || draggingObject.expired() || mainWindow()->selectedTool >= TOOL_SMOOTH) return; QPoint position = evt->pos(); glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); std::optional rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000, [](std::shared_ptr part) { - return (part == draggingObject->lock()) ? FilterResult::PASS : FilterResult::TARGET; + return (part == draggingObject.lock()) ? FilterResult::PASS : FilterResult::TARGET; }); if (!rayHit) return; @@ -133,13 +133,13 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) { Data::Vector3 vec = rayHit->worldPoint + (tFormedInitialPos - tFormedHitPos); // The part being dragged's frame local to the hit target's frame, but without its position component // To find a world vector local to the new frame, use newFrame, not localFrame, as localFrame is localFrame is local to targetFrame in itself - Data::CFrame localFrame = (targetFrame.Inverse() * (draggingObject->lock()->cframe.Rotation() + vec)); + Data::CFrame localFrame = (targetFrame.Inverse() * (draggingObject.lock()->cframe.Rotation() + vec)); // Snap axis localFrame = snapCFrame(localFrame); // Snap to studs - Data::Vector3 draggingPartSize = draggingObject->lock()->size; + Data::Vector3 draggingPartSize = draggingObject.lock()->size; glm::vec3 inverseNormalPartSize = (Data::Vector3)(partSize - glm::vec3(localFrame.Rotation() * draggingPartSize)) * inverseSurfaceNormal / 2.f; if (snappingFactor() > 0) localFrame = localFrame.Rotation() + glm::round(glm::vec3(localFrame.Position() * inverseSurfaceNormal - inverseNormalPartSize) / snappingFactor()) * snappingFactor() + inverseNormalPartSize @@ -149,13 +149,13 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) { // Unsink the object // Get the normal of the surface relative to the part's frame, and get the size along that vector - Data::Vector3 unsinkOffset = newFrame.Rotation() * ((newFrame.Rotation().Inverse() * rayHit->worldNormal) * draggingObject->lock()->size / 2); + Data::Vector3 unsinkOffset = newFrame.Rotation() * ((newFrame.Rotation().Inverse() * rayHit->worldNormal) * draggingObject.lock()->size / 2); - draggingObject->lock()->cframe = newFrame + unsinkOffset; + draggingObject.lock()->cframe = newFrame + unsinkOffset; - gWorkspace()->SyncPartPhysics(draggingObject->lock()); - sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position()); + gWorkspace()->SyncPartPhysics(draggingObject.lock()); + sendPropertyUpdatedSignal(draggingObject.lock(), "Position", draggingObject.lock()->position()); } inline glm::vec3 vec3fy(glm::vec4 vec) { @@ -164,11 +164,11 @@ inline glm::vec3 vec3fy(glm::vec4 vec) { // Taken from Godot's implementation of moving handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp) void MainGLWidget::handleLinearTransform(QMouseEvent* evt) { - if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return; + if (!isMouseDragging || !draggingHandle|| editorToolHandles->adornee.expired() || !editorToolHandles->active) return; QPoint position = evt->pos(); - auto part = editorToolHandles->adornee->lock(); + auto part = editorToolHandles->adornee.lock(); // This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor @@ -196,7 +196,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) { glm::vec3 centerPoint = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position(); // Apply snapping in the current frame - glm::vec3 diff = centerPoint - (glm::vec3)editorToolHandles->adornee->lock()->position(); + glm::vec3 diff = centerPoint - (glm::vec3)part->position(); if (snappingFactor()) diff = frame.Rotation() * (glm::round(glm::vec3(frame.Inverse().Rotation() * diff) / snappingFactor()) * snappingFactor()); Data::Vector3 oldSize = part->size; @@ -204,7 +204,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) { switch (mainWindow()->selectedTool) { case TOOL_MOVE: { // Add difference - editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff; + part->cframe = part->cframe + diff; } break; case TOOL_SCALE: { @@ -241,18 +241,18 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) { if (mainWindow()->editSoundEffects && (oldSize != part->size) && QFile::exists("./assets/excluded/switch.wav")) playSound("./assets/excluded/switch.wav"); - gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast(editorToolHandles->adornee->lock())); - sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position()); - sendPropertyUpdatedSignal(draggingObject->lock(), "Size", Data::Vector3(draggingObject->lock()->size)); + gWorkspace()->SyncPartPhysics(part); + sendPropertyUpdatedSignal(part, "Position", part->position()); + sendPropertyUpdatedSignal(part, "Size", Data::Vector3(part->size)); } // Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490) glm::vec2 startPoint; void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) { - if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return; + if (!isMouseDragging || !draggingHandle || editorToolHandles->adornee.expired() || !editorToolHandles->active) return; glm::vec2 destPoint = glm::vec2(evt->pos().x(), evt->pos().y()); - auto part = editorToolHandles->adornee->lock(); + auto part = editorToolHandles->adornee.lock(); // Calculate part pos as screen point glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 1000.0f); @@ -284,12 +284,12 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) { part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles); - gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast(editorToolHandles->adornee->lock())); - sendPropertyUpdatedSignal(draggingObject->lock(), "Rotation", draggingObject->lock()->cframe.ToEulerAnglesXYZ()); + gWorkspace()->SyncPartPhysics(part); + sendPropertyUpdatedSignal(draggingObject.lock(), "Rotation", draggingObject.lock()->cframe.ToEulerAnglesXYZ()); } std::optional MainGLWidget::raycastHandle(glm::vec3 pointDir) { - if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return std::nullopt; + if (editorToolHandles->adornee.expired() || !editorToolHandles->active) return std::nullopt; return editorToolHandles->RaycastHandle(rp3d::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000)); } @@ -353,7 +353,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { auto handle = raycastHandle(pointDir); if (handle.has_value()) { startPoint = glm::vec2(evt->pos().x(), evt->pos().y()); - initialFrame = editorToolHandles->adornee->lock()->cframe; + initialFrame = editorToolHandles->adornee.lock()->cframe; isMouseDragging = true; draggingHandle = handle; return; @@ -417,10 +417,10 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { } void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) { - // if (isMouseDragging) draggingObject->lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11); + // if (isMouseDragging) draggingObject.lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11); isMouseRightDragging = false; isMouseDragging = false; - draggingObject = std::nullopt; + draggingObject = {}; draggingHandle = std::nullopt; } diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index d8ae5d3..1292ecc 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -100,7 +100,7 @@ MainWindow::MainWindow(QWidget *parent) // Update handles addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { - editorToolHandles->adornee = std::nullopt; + editorToolHandles->adornee = {}; if (newSelection.size() == 0) return; InstanceRef inst = newSelection[0].lock(); if (inst->GetClass() != &Part::TYPE) return; diff --git a/editor/panes/propertiesview.cpp b/editor/panes/propertiesview.cpp index 57fa3e1..2e25d35 100644 --- a/editor/panes/propertiesview.cpp +++ b/editor/panes/propertiesview.cpp @@ -36,8 +36,8 @@ public: QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (index.column() == 0) return nullptr; - if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return nullptr; - InstanceRef inst = view->currentInstance->lock(); + if (!index.parent().isValid() || view->currentInstance.expired()) return nullptr; + InstanceRef inst = view->currentInstance.lock(); // If the property is deeper than 1 layer, then it is considered composite // Handle specially @@ -105,8 +105,8 @@ public: void setEditorData(QWidget *editor, const QModelIndex &index) const override { if (index.column() == 0) return; - if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return; - InstanceRef inst = view->currentInstance->lock(); + if (!index.parent().isValid() || view->currentInstance.expired()) return; + InstanceRef inst = view->currentInstance.lock(); bool isComposite = index.parent().parent().isValid(); std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : ""; @@ -157,8 +157,8 @@ public: void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { if (index.column() == 0) return; - if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return; - InstanceRef inst = view->currentInstance->lock(); + if (!index.parent().isValid() || view->currentInstance.expired()) return; + InstanceRef inst = view->currentInstance.lock(); bool isComposite = index.parent().parent().isValid(); std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : ""; @@ -268,6 +268,7 @@ void PropertiesView::drawBranches(QPainter *painter, const QRect &rect, const QM void PropertiesView::setSelected(std::optional instance) { clear(); + currentInstance = {}; if (!instance) return; InstanceRef inst = instance.value(); currentInstance = inst; @@ -334,8 +335,8 @@ void PropertiesView::setSelected(std::optional instance) { } void PropertiesView::propertyChanged(QTreeWidgetItem *item, int column) { - if (!item->parent() || (item->parent() && item->parent()->parent()) || !currentInstance || currentInstance->expired()) return; - InstanceRef inst = currentInstance->lock(); + if (!item->parent() || (item->parent() && item->parent()->parent()) || currentInstance.expired()) return; + InstanceRef inst = currentInstance.lock(); std::string propertyName = item->data(0, Qt::DisplayRole).toString().toStdString(); PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect(); diff --git a/editor/panes/propertiesview.h b/editor/panes/propertiesview.h index 6460b1d..2a47886 100644 --- a/editor/panes/propertiesview.h +++ b/editor/panes/propertiesview.h @@ -11,7 +11,7 @@ namespace Data { class Variant; }; class PropertiesView : public QTreeWidget { Q_DECLARE_PRIVATE(QTreeView) - std::optional currentInstance; + InstanceRefWeak currentInstance; void propertyChanged(QTreeWidgetItem *item, int column); void activateProperty(QTreeWidgetItem *item, int column); void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);