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 "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 = {
|
||||
|
@ -348,4 +353,73 @@ DescendantsIterator::self_type DescendantsIterator::operator++(int _) {
|
|||
current = current->GetParent().value()->GetChildren()[siblingIndex.back()];
|
||||
|
||||
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
|
||||
|
||||
#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;
|
||||
|
|
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");
|
||||
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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue