feat(instance): added clone method
This commit is contained in:
parent
c4ad7d5620
commit
cab5ec0514
4 changed files with 99 additions and 0 deletions
|
@ -2,8 +2,10 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "datatypes/meta.h"
|
#include "datatypes/meta.h"
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
|
#include "datatypes/ref.h"
|
||||||
#include "error/instance.h"
|
#include "error/instance.h"
|
||||||
#include "objects/base/member.h"
|
#include "objects/base/member.h"
|
||||||
|
#include "objects/base/refstate.h"
|
||||||
#include "objects/meta.h"
|
#include "objects/meta.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "panic.h"
|
#include "panic.h"
|
||||||
|
@ -12,6 +14,9 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// Static so that this variable name is "local" to this source file
|
// Static so that this variable name is "local" to this source file
|
||||||
const InstanceType Instance::TYPE = {
|
const InstanceType Instance::TYPE = {
|
||||||
|
@ -348,4 +353,73 @@ DescendantsIterator::self_type DescendantsIterator::operator++(int _) {
|
||||||
current = current->GetParent().value()->GetChildren()[siblingIndex.back()];
|
current = current->GetParent().value()->GetChildren()[siblingIndex.back()];
|
||||||
|
|
||||||
return *this;
|
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;
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
#include "error/instance.h"
|
#include "error/instance.h"
|
||||||
#include "error/result.h"
|
#include "error/result.h"
|
||||||
#include "member.h"
|
#include "member.h"
|
||||||
|
#include "objects/base/refstate.h"
|
||||||
|
|
||||||
class Instance;
|
class Instance;
|
||||||
typedef std::shared_ptr<Instance>(*InstanceConstructor)();
|
typedef std::shared_ptr<Instance>(*InstanceConstructor)();
|
||||||
|
@ -36,6 +38,7 @@ struct InstanceType {
|
||||||
InstanceFlags flags;
|
InstanceFlags flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::pair<std::shared_ptr<Instance>, std::string> _RefStatePropertyCell;
|
||||||
|
|
||||||
class DescendantsIterator;
|
class DescendantsIterator;
|
||||||
|
|
||||||
|
@ -99,6 +102,7 @@ public:
|
||||||
void UpdateProperty(std::string name);
|
void UpdateProperty(std::string name);
|
||||||
// Returning a list of property names feels kinda janky. Is this really the way to go?
|
// Returning a list of property names feels kinda janky. Is this really the way to go?
|
||||||
std::vector<std::string> GetProperties();
|
std::vector<std::string> GetProperties();
|
||||||
|
std::vector<std::pair<std::string, std::shared_ptr<Instance>>> GetReferenceProperties();
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
result<std::shared_ptr<T>, InstanceCastError> CastTo() {
|
result<std::shared_ptr<T>, InstanceCastError> CastTo() {
|
||||||
|
@ -112,6 +116,7 @@ public:
|
||||||
// Serialization
|
// Serialization
|
||||||
void Serialize(pugi::xml_node parent);
|
void Serialize(pugi::xml_node parent);
|
||||||
static result<std::shared_ptr<Instance>, NoSuchInstance> Deserialize(pugi::xml_node node);
|
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;
|
typedef std::shared_ptr<Instance> InstanceRef;
|
||||||
|
|
17
core/src/objects/base/refstate.h
Normal file
17
core/src/objects/base/refstate.h
Normal 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>>;
|
|
@ -462,6 +462,9 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
||||||
Logger::warning("warning message");
|
Logger::warning("warning message");
|
||||||
if (evt->key() == Qt::Key_O)
|
if (evt->key() == Qt::Key_O)
|
||||||
Logger::error("error message");
|
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) {
|
void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue