refactor: replaced optional<weak_ptr<T>> with weak_ptr<T> directly

This commit is contained in:
maelstrom 2025-04-17 16:34:47 +02:00
parent 8c2474abbf
commit 2569e3f56f
11 changed files with 84 additions and 74 deletions

View file

@ -56,6 +56,16 @@ bool operator ==(std::optional<std::weak_ptr<T>> a, std::optional<std::weak_ptr<
|| (a.has_value() && !a.value().expired()) && (b.has_value() && !b.value().expired()) && a.value().lock() == b.value().lock();
}
template <typename T>
bool operator ==(std::weak_ptr<T> a, std::weak_ptr<T> b) {
return a.expired() && b.expired() || (!a.expired() && !b.expired() && a.lock() == b.lock());
}
template <typename T>
std::weak_ptr<T> optional_to_weak(std::optional<std::shared_ptr<T>> a) {
return a ? a.value() : std::weak_ptr<T>();
}
// TODO: Test this
bool Instance::ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent) {
for (std::optional<std::shared_ptr<Instance>> currentParent = newParent; currentParent.has_value(); currentParent = currentParent.value()->GetParent()) {
@ -72,15 +82,15 @@ bool Instance::SetParent(std::optional<std::shared_ptr<Instance>> 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<std::shared_ptr<Instance>> 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<DataModel>(GetParent().value())) : GetParent().value()->dataModel();
this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::make_optional(std::dynamic_pointer_cast<Workspace>(GetParent().value())) : GetParent().value()->workspace();
this->_dataModel = GetParent().value()->GetClass() == &DataModel::TYPE ? std::dynamic_pointer_cast<DataModel>(GetParent().value()) : GetParent().value()->_dataModel;
this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::dynamic_pointer_cast<Workspace>(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<std::shared_ptr<Instance>> updatedCh
}
std::optional<std::shared_ptr<DataModel>> 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<std::shared_ptr<Workspace>> 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<std::shared_ptr<Instance>> 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<Instance> DUMMY_INSTANCE;

View file

@ -45,13 +45,13 @@ class DescendantsIterator;
// https://stackoverflow.com/q/56415222/16255372
class Instance : public std::enable_shared_from_this<Instance> {
private:
std::optional<std::weak_ptr<Instance>> parent;
std::weak_ptr<Instance> parent;
std::vector<std::shared_ptr<Instance>> children;
std::optional<std::vector<std::string>> cachedMemberList;
std::optional<std::weak_ptr<DataModel>> _dataModel;
std::optional<std::weak_ptr<Workspace>> _workspace;
std::weak_ptr<DataModel> _dataModel;
std::weak_ptr<Workspace> _workspace;
bool ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent);
void updateAncestry(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent);

View file

@ -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<HandleFace> Handles::RaycastHandle(rp3d::Ray ray) {

View file

@ -36,11 +36,11 @@ public:
bool nixAxes = false; // XYZ -> ZXY, used with rotation
bool active;
std::optional<std::weak_ptr<Part>> adornee;
std::weak_ptr<Part> adornee;
HandlesType handlesType;
// inline std::optional<std::weak_ptr<Part>> GetAdornee() { return adornee; }
// inline void SetAdornee(std::optional<std::weak_ptr<Part>> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
// inline std::weak_ptr<Part> GetAdornee() { return adornee; }
// inline void SetAdornee(std::weak_ptr<Part> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
Handles();

View file

@ -22,18 +22,18 @@ Snap::~Snap() {
}
void Snap::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> 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<rp::FixedJoint*>(workspace().value()->physicsWorld->createJoint(jointInfo));
}

View file

@ -14,8 +14,8 @@ protected:
public:
const static InstanceType TYPE;
std::optional<std::weak_ptr<Part>> part0;
std::optional<std::weak_ptr<Part>> part1;
std::weak_ptr<Part> part0;
std::weak_ptr<Part> part1;
Data::CFrame c0;
Data::CFrame c1;

View file

@ -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);

View file

@ -44,7 +44,7 @@ void MainGLWidget::resizeGL(int w, int h) {
glm::vec2 firstPoint;
glm::vec2 secondPoint;
extern std::optional<std::weak_ptr<Part>> draggingObject;
extern std::weak_ptr<Part> draggingObject;
extern std::optional<HandleFace> draggingHandle;
extern Shader* shader;
void MainGLWidget::paintGL() {
@ -107,19 +107,19 @@ Data::CFrame snapCFrame(Data::CFrame frame) {
}
bool isMouseDragging = false;
std::optional<std::weak_ptr<Part>> draggingObject;
std::weak_ptr<Part> draggingObject;
std::optional<HandleFace> 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<const RaycastResult> rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000, [](std::shared_ptr<Part> 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<Part>(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<Part>(editorToolHandles->adornee->lock()));
sendPropertyUpdatedSignal(draggingObject->lock(), "Rotation", draggingObject->lock()->cframe.ToEulerAnglesXYZ());
gWorkspace()->SyncPartPhysics(part);
sendPropertyUpdatedSignal(draggingObject.lock(), "Rotation", draggingObject.lock()->cframe.ToEulerAnglesXYZ());
}
std::optional<HandleFace> 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;
}

View file

@ -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;

View file

@ -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<InstanceRef> instance) {
clear();
currentInstance = {};
if (!instance) return;
InstanceRef inst = instance.value();
currentInstance = inst;
@ -334,8 +335,8 @@ void PropertiesView::setSelected(std::optional<InstanceRef> 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();

View file

@ -11,7 +11,7 @@ namespace Data { class Variant; };
class PropertiesView : public QTreeWidget {
Q_DECLARE_PRIVATE(QTreeView)
std::optional<InstanceRefWeak> currentInstance;
InstanceRefWeak currentInstance;
void propertyChanged(QTreeWidgetItem *item, int column);
void activateProperty(QTreeWidgetItem *item, int column);
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);