feat(instance): added clone method

This commit is contained in:
maelstrom 2025-04-20 01:04:59 +02:00
parent c4ad7d5620
commit cab5ec0514
4 changed files with 99 additions and 0 deletions

View file

@ -2,8 +2,10 @@
#include "common.h"
#include "datatypes/meta.h"
#include "datatypes/base.h"
#include "datatypes/ref.h"
#include "error/instance.h"
#include "objects/base/member.h"
#include "objects/base/refstate.h"
#include "objects/meta.h"
#include "logger.h"
#include "panic.h"
@ -12,6 +14,9 @@
#include <cstdio>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
// Static so that this variable name is "local" to this source file
const InstanceType Instance::TYPE = {
@ -349,3 +354,72 @@ DescendantsIterator::self_type DescendantsIterator::operator++(int _) {
return *this;
}
std::optional<std::shared_ptr<Instance>> Instance::Clone(RefState<_RefStatePropertyCell> state) {
std::shared_ptr<Instance> newInstance = GetClass()->constructor();
// Copy properties
for (std::string property : GetProperties()) {
PropertyMeta meta = GetPropertyMeta(property).expect();
if (meta.flags & (PROP_READONLY | PROP_NOSAVE)) continue;
// Update InstanceRef properties using map above
if (meta.type == &Data::InstanceRef::TYPE) {
std::weak_ptr<Instance> refWeak = GetPropertyValue(property).expect().get<Data::InstanceRef>();
if (refWeak.expired()) continue;
auto ref = refWeak.lock();
auto remappedRef = state->remappedInstances[ref]; // TODO: I think this is okay? Maybe?? Add null check?
if (remappedRef) {
// If the instance has already been remapped, set the new value
newInstance->SetPropertyValue(property, Data::InstanceRef(remappedRef)).expect();
} else {
// Otheriise, queue this property to be updated later, and keep its current value
auto& refs = state->refsAwaitingRemap[ref];
refs.push_back(std::make_pair(ref, property));
state->refsAwaitingRemap[ref] = refs;
newInstance->SetPropertyValue(property, Data::InstanceRef(ref)).expect();
}
} else {
Data::Variant value = GetPropertyValue(property).expect();
newInstance->SetPropertyValue(property, value).expect();
}
}
// Remap self
state->remappedInstances[shared_from_this()] = newInstance;
// Remap queued properties
for (std::pair<std::shared_ptr<Instance>, std::string> ref : state->refsAwaitingRemap[shared_from_this()]) {
ref.first->SetPropertyValue(ref.second, Data::InstanceRef(newInstance)).expect();
}
// Clone children
for (std::shared_ptr<Instance> child : GetChildren()) {
std::optional<std::shared_ptr<Instance>> clonedChild = child->Clone(state);
if (clonedChild)
newInstance->AddChild(clonedChild.value());
}
return newInstance;
}
std::vector<std::pair<std::string, std::shared_ptr<Instance>>> Instance::GetReferenceProperties() {
std::vector<std::pair<std::string, std::shared_ptr<Instance>>> referenceProperties;
auto propertyNames = GetProperties();
for (std::string property : propertyNames) {
PropertyMeta meta = GetPropertyMeta(property).expect();
if (meta.type != &Data::InstanceRef::TYPE) continue;
std::weak_ptr<Instance> ref = GetPropertyValue(property).expect().get<Data::InstanceRef>();
if (ref.expired()) continue;
referenceProperties.push_back(std::make_pair(property, ref.lock()));
}
return referenceProperties;
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <iterator>
#include <utility>
#include <vector>
#include <memory>
#include <optional>
@ -14,6 +15,7 @@
#include "error/instance.h"
#include "error/result.h"
#include "member.h"
#include "objects/base/refstate.h"
class Instance;
typedef std::shared_ptr<Instance>(*InstanceConstructor)();
@ -36,6 +38,7 @@ struct InstanceType {
InstanceFlags flags;
};
typedef std::pair<std::shared_ptr<Instance>, std::string> _RefStatePropertyCell;
class DescendantsIterator;
@ -99,6 +102,7 @@ public:
void UpdateProperty(std::string name);
// Returning a list of property names feels kinda janky. Is this really the way to go?
std::vector<std::string> GetProperties();
std::vector<std::pair<std::string, std::shared_ptr<Instance>>> GetReferenceProperties();
template <typename T>
result<std::shared_ptr<T>, InstanceCastError> CastTo() {
@ -112,6 +116,7 @@ public:
// Serialization
void Serialize(pugi::xml_node parent);
static result<std::shared_ptr<Instance>, NoSuchInstance> Deserialize(pugi::xml_node node);
std::optional<std::shared_ptr<Instance>> Clone(RefState<_RefStatePropertyCell> state = std::make_shared<__RefState<_RefStatePropertyCell>>());
};
typedef std::shared_ptr<Instance> InstanceRef;

View file

@ -0,0 +1,17 @@
#pragma once
// Helper struct used for remapping reference when cloning/serializing
#include <map>
#include <memory>
#include <vector>
class Instance;
template <typename T>
struct __RefState {
std::map<std::shared_ptr<Instance>, std::shared_ptr<Instance>> remappedInstances;
std::map<std::shared_ptr<Instance>, std::vector<T>> refsAwaitingRemap;
};
template <typename T>
using RefState = std::shared_ptr<__RefState<T>>;

View file

@ -462,6 +462,9 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
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].expired())
getSelection()[0].lock()->Clone().value()->SetParent(gWorkspace());
}
void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) {