add Node/Network
This commit is contained in:
parent
ec74a35dfc
commit
493b7262a7
@ -44,18 +44,29 @@ target_sources(nf7
|
||||
nf7.cc
|
||||
nf7.hh
|
||||
|
||||
common/aggregate_command.hh
|
||||
common/dir.hh
|
||||
common/dir_item.hh
|
||||
common/file_ref.hh
|
||||
common/generic_context.hh
|
||||
common/generic_history.hh
|
||||
common/generic_memento.hh
|
||||
common/generic_type_info.hh
|
||||
common/gui_window.hh
|
||||
common/history.hh
|
||||
common/lambda.hh
|
||||
common/memento.hh
|
||||
common/node.hh
|
||||
common/node_adaptor.hh
|
||||
common/node_link_store.hh
|
||||
common/logger.hh
|
||||
common/logger_pool.hh
|
||||
common/ptr_selector.hh
|
||||
common/queue.hh
|
||||
common/value.hh
|
||||
common/yas.hh
|
||||
|
||||
file/node_network.cc
|
||||
file/system_dir.cc
|
||||
file/system_imgui_config.cc
|
||||
file/system_logger.cc
|
||||
|
76
common/aggregate_command.hh
Normal file
76
common/aggregate_command.hh
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/history.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
template <typename T = History::Command>
|
||||
class AggregateCommand : public T {
|
||||
public:
|
||||
using CommandList = std::vector<std::unique_ptr<T>>;
|
||||
|
||||
AggregateCommand(CommandList&& commands) noexcept :
|
||||
commands_(std::move(commands)) {
|
||||
}
|
||||
|
||||
void Apply() override {
|
||||
auto itr = commands_.begin();
|
||||
try {
|
||||
try {
|
||||
while (itr < commands_.end()) {
|
||||
(*itr)->Apply();
|
||||
++itr;
|
||||
}
|
||||
} catch (History::CorruptException&) {
|
||||
throw History::CorruptException("failed to apply AggregateCommand");
|
||||
}
|
||||
} catch (History::CorruptException&) {
|
||||
try {
|
||||
while (itr > commands_.begin()) {
|
||||
--itr;
|
||||
(*itr)->Revert();
|
||||
}
|
||||
} catch (History::CorruptException&) {
|
||||
throw History::CorruptException(
|
||||
"AggregateCommand gave up recovering from failure of apply");
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
void Revert() override {
|
||||
auto itr = commands_.rbegin();
|
||||
try {
|
||||
try {
|
||||
while (itr < commands_.rend()) {
|
||||
(*itr)->Revert();
|
||||
++itr;
|
||||
}
|
||||
} catch (History::CorruptException&) {
|
||||
throw History::CorruptException("failed to revert AggregateCommand");
|
||||
}
|
||||
} catch (History::CorruptException&) {
|
||||
try {
|
||||
while (itr > commands_.rbegin()) {
|
||||
--itr;
|
||||
(*itr)->Apply();
|
||||
}
|
||||
} catch (History::CorruptException&) {
|
||||
throw History::CorruptException(
|
||||
"AggregateCommand gave up recovering from failure of revert");
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::span<const std::unique_ptr<T>> commands() const noexcept { return commands_; }
|
||||
|
||||
private:
|
||||
CommandList commands_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
59
common/generic_history.hh
Normal file
59
common/generic_history.hh
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/history.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
template <typename T>
|
||||
class GenericHistory : public History {
|
||||
public:
|
||||
GenericHistory() = delete;
|
||||
GenericHistory(Env& env) noexcept : env_(&env) {
|
||||
}
|
||||
GenericHistory(const GenericHistory&) = delete;
|
||||
GenericHistory(GenericHistory&&) = default;
|
||||
GenericHistory& operator=(const GenericHistory&) = delete;
|
||||
GenericHistory& operator=(GenericHistory&&) = default;
|
||||
|
||||
void Add(std::unique_ptr<T>&& cmd) noexcept {
|
||||
cmds_.erase(cmds_.begin()+static_cast<intmax_t>(cursor_), cmds_.end());
|
||||
cmds_.push_back(std::move(cmd));
|
||||
cursor_++;
|
||||
}
|
||||
void Clear() noexcept {
|
||||
cmds_.clear();
|
||||
}
|
||||
|
||||
void ReDo() {
|
||||
if (cursor_ <= 0) return;
|
||||
cmds_[cursor_-1]->Revert();
|
||||
--cursor_;
|
||||
}
|
||||
void UnDo() {
|
||||
if (cursor_ >= cmds_.size()) return;
|
||||
cmds_[cursor_]->Apply();
|
||||
++cursor_;
|
||||
}
|
||||
|
||||
T* next() const noexcept {
|
||||
return cursor_ > 0? cmds_[cursor_-1].get(): nullptr;
|
||||
}
|
||||
T* prev() const noexcept {
|
||||
return cursor_ < cmds_.size()? cmds_[cursor_].get(): nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Env* const env_;
|
||||
|
||||
std::vector<std::unique_ptr<T>> cmds_;
|
||||
|
||||
size_t cursor_ = 0;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
73
common/generic_memento.hh
Normal file
73
common/generic_memento.hh
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "common/memento.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
template <typename T>
|
||||
class GenericMemento : public Memento {
|
||||
public:
|
||||
GenericMemento(File& owner, T&& data) noexcept :
|
||||
owner_(&owner), data_(std::move(data)) {
|
||||
}
|
||||
~GenericMemento() noexcept {
|
||||
assert(map_.empty());
|
||||
}
|
||||
|
||||
std::shared_ptr<Tag> Save() noexcept override {
|
||||
if (tag_) return tag_;
|
||||
auto [itr, emplaced] = map_.emplace(next_++, data_);
|
||||
assert(emplaced);
|
||||
return tag_ = std::make_shared<CustomTag>(*this, itr->first);
|
||||
}
|
||||
void Restore(const std::shared_ptr<Tag>& tag) override {
|
||||
assert(tag);
|
||||
auto itr = map_.find(tag->id());
|
||||
assert(itr < map_.end());
|
||||
data_ = itr->second;
|
||||
tag_ = tag;
|
||||
|
||||
if (owner_->id()) {
|
||||
owner_->env().Handle(
|
||||
{.id = owner_->id(), .type = File::Event::kUpdate});
|
||||
}
|
||||
}
|
||||
void Commit() noexcept {
|
||||
tag_ = nullptr;
|
||||
|
||||
if (owner_->id()) {
|
||||
owner_->env().Handle(
|
||||
{.id = owner_->id(), .type = File::Event::kUpdate});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
File* const owner_;
|
||||
|
||||
T data_;
|
||||
|
||||
Tag::Id next_ = 0;
|
||||
std::unordered_map<Tag::Id, T> map_;
|
||||
|
||||
std::shared_ptr<CustomTag> tag_;
|
||||
|
||||
|
||||
class CustomTag final : public Tag {
|
||||
public:
|
||||
CustomTag(GenericMemento& owner, Id id) noexcept : Tag(id), owner_(&owner) {
|
||||
}
|
||||
~CustomTag() noexcept {
|
||||
owner_->data_.erase(id());
|
||||
}
|
||||
private:
|
||||
GenericMemento* owner_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace nf7
|
41
common/history.hh
Normal file
41
common/history.hh
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class History {
|
||||
public:
|
||||
class Command;
|
||||
class CorruptException;
|
||||
|
||||
History() = default;
|
||||
virtual ~History() = default;
|
||||
History(const History&) = delete;
|
||||
History(History&&) = delete;
|
||||
History& operator=(const History&) = delete;
|
||||
History& operator=(History&&) = delete;
|
||||
|
||||
virtual void UnDo() = 0;
|
||||
virtual void ReDo() = 0;
|
||||
};
|
||||
|
||||
class History::Command {
|
||||
public:
|
||||
Command() = default;
|
||||
virtual ~Command() = default;
|
||||
Command(const Command&) = delete;
|
||||
Command(Command&&) = delete;
|
||||
Command& operator=(const Command&) = delete;
|
||||
Command& operator=(Command&&) = delete;
|
||||
|
||||
virtual void Apply() = 0;
|
||||
virtual void Revert() = 0;
|
||||
};
|
||||
|
||||
class History::CorruptException : public Exception {
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
44
common/lambda.hh
Normal file
44
common/lambda.hh
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/value.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Lambda : public File::Interface {
|
||||
public:
|
||||
class Context;
|
||||
|
||||
Lambda() = default;
|
||||
Lambda(const Lambda&) = default;
|
||||
Lambda(Lambda&&) = default;
|
||||
Lambda& operator=(const Lambda&) = delete;
|
||||
Lambda& operator=(Lambda&&) = delete;
|
||||
|
||||
virtual std::shared_ptr<Context> CreateContext() noexcept = 0;
|
||||
};
|
||||
|
||||
class Lambda::Context : public nf7::Context {
|
||||
public:
|
||||
using nf7::Context::Context;
|
||||
|
||||
virtual void Initialize(const std::shared_ptr<Lambda::Context>& self,
|
||||
const std::shared_ptr<Lambda::Context>& parent) {
|
||||
self_ = self;
|
||||
(void) parent;
|
||||
}
|
||||
|
||||
virtual void Handle(
|
||||
size_t idx, Value&&, const std::shared_ptr<Lambda::Context>&) = 0;
|
||||
|
||||
std::shared_ptr<Lambda::Context> self() const noexcept { return self_.lock(); }
|
||||
|
||||
private:
|
||||
std::weak_ptr<Lambda::Context> self_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
70
common/memento.hh
Normal file
70
common/memento.hh
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Memento : public File::Interface {
|
||||
public:
|
||||
class Tag;
|
||||
class RestoreCommand;
|
||||
class CorruptException;
|
||||
|
||||
Memento() = default;
|
||||
Memento(const Memento&) = delete;
|
||||
Memento(Memento&&) = delete;
|
||||
Memento& operator=(const Memento&) = delete;
|
||||
Memento& operator=(Memento&&) = delete;
|
||||
|
||||
virtual std::shared_ptr<Tag> Save() noexcept = 0;
|
||||
virtual void Restore(const std::shared_ptr<Tag>&) noexcept = 0;
|
||||
};
|
||||
|
||||
class Memento::Tag {
|
||||
public:
|
||||
using Id = uint64_t;
|
||||
|
||||
Tag() = delete;
|
||||
Tag(Id id) noexcept : id_(id) {
|
||||
}
|
||||
virtual ~Tag() = default;
|
||||
Tag(const Tag&) = default;
|
||||
Tag(Tag&&) = default;
|
||||
Tag& operator=(const Tag&) = delete;
|
||||
Tag& operator=(Tag&&) = delete;
|
||||
|
||||
Id id() const noexcept { return id_; }
|
||||
|
||||
private:
|
||||
Id id_;
|
||||
};
|
||||
|
||||
class Memento::RestoreCommand : public History::Command {
|
||||
public:
|
||||
RestoreCommand(Memento& mem, const std::shared_ptr<Tag>& tag) noexcept :
|
||||
mem_(&mem), tag_(tag) {
|
||||
}
|
||||
|
||||
void Apply() override { Exec(); }
|
||||
void Revert() override { Exec(); }
|
||||
|
||||
private:
|
||||
Memento* const mem_;
|
||||
std::shared_ptr<Tag> tag_;
|
||||
|
||||
void Exec() {
|
||||
mem_->Restore(std::exchange(tag_, mem_->Save()));
|
||||
}
|
||||
};
|
||||
|
||||
class Memento::CorruptException : public Exception {
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
78
common/node.hh
Normal file
78
common/node.hh
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/lambda.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Node : public File::Interface {
|
||||
public:
|
||||
class Editor;
|
||||
|
||||
using Id = uint64_t;
|
||||
|
||||
Node() = default;
|
||||
Node(const Node&) = default;
|
||||
Node(Node&&) = default;
|
||||
Node& operator=(const Node&) = default;
|
||||
Node& operator=(Node&&) = default;
|
||||
|
||||
virtual void UpdateNode(Editor&) noexcept = 0;
|
||||
|
||||
std::span<const std::string> input() const noexcept { return input_; }
|
||||
std::span<const std::string> output() const noexcept { return output_; }
|
||||
const std::string& input(size_t i) const noexcept { return input_[i]; }
|
||||
const std::string& output(size_t i) const noexcept { return output_[i]; }
|
||||
|
||||
size_t input(std::string_view name) const {
|
||||
auto itr = std::find(input_.begin(), input_.end(), name);
|
||||
if (itr >= input_.end()) {
|
||||
throw Exception("missing input socket: "+std::string(name));
|
||||
}
|
||||
return static_cast<size_t>(itr - input_.begin());
|
||||
}
|
||||
size_t output(std::string_view name) const {
|
||||
auto itr = std::find(output_.begin(), output_.end(), name);
|
||||
if (itr >= output_.end()) {
|
||||
throw Exception("missing output socket: "+std::string(name));
|
||||
}
|
||||
return static_cast<size_t>(itr - output_.begin());
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::string> input_;
|
||||
std::vector<std::string> output_;
|
||||
};
|
||||
|
||||
class Node::Editor {
|
||||
public:
|
||||
Editor() = default;
|
||||
virtual ~Editor() = default;
|
||||
Editor(const Editor&) = delete;
|
||||
Editor(Editor&&) = delete;
|
||||
Editor& operator=(const Editor&) = delete;
|
||||
Editor& operator=(Editor&&) = delete;
|
||||
|
||||
virtual void AddLink(Node& src_node, std::string_view src_name,
|
||||
Node& dst_node, std::string_view dst_name) noexcept = 0;
|
||||
virtual void RemoveLink(Node& src_node, std::string_view src_name,
|
||||
Node& dst_node, std::string_view dst_name) noexcept = 0;
|
||||
|
||||
virtual std::vector<std::pair<Node*, std::string>> GetSrcOf(Node&, std::string_view) const noexcept = 0;
|
||||
virtual std::vector<std::pair<Node*, std::string>> GetDstOf(Node&, std::string_view) const noexcept = 0;
|
||||
|
||||
virtual std::shared_ptr<Lambda::Context> ctx() noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
28
common/node_adaptor.hh
Normal file
28
common/node_adaptor.hh
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/node.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class NodeAdaptor : public nf7::Node {
|
||||
public:
|
||||
NodeAdaptor() = delete;
|
||||
NodeAdaptor(File& file) : file_(&file) {
|
||||
}
|
||||
NodeAdaptor(const NodeAdaptor&) = delete;
|
||||
NodeAdaptor(NodeAdaptor&&) = delete;
|
||||
NodeAdaptor& operator=(const NodeAdaptor&) = delete;
|
||||
NodeAdaptor& operator=(NodeAdaptor&&) = delete;
|
||||
|
||||
void UpdateNode(Node::Editor&) noexcept override {
|
||||
ImGui::TextUnformatted("not implemented");
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::File* const file_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
120
common/node_link_store.hh
Normal file
120
common/node_link_store.hh
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/utility/usertype.hpp>
|
||||
|
||||
#include "common/aggregate_command.hh"
|
||||
#include "common/history.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class NodeLinkStore {
|
||||
public:
|
||||
class SwapCommand;
|
||||
|
||||
struct Link {
|
||||
public:
|
||||
uint64_t src_id;
|
||||
std::string src_name;
|
||||
uint64_t dst_id;
|
||||
std::string dst_name;
|
||||
|
||||
bool operator==(const Link& other) const noexcept {
|
||||
if (src_id && other.src_id && src_id != other.src_id) return false;
|
||||
if (dst_id && other.dst_id && dst_id != other.dst_id) return false;
|
||||
if (src_name.size() && other.src_name.size() && src_name != other.src_name) return false;
|
||||
if (dst_name.size() && other.dst_name.size() && dst_name != other.dst_name) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Ar>
|
||||
Ar& serialize(Ar& ar) {
|
||||
ar(src_id, src_name, dst_id, dst_name);
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
NodeLinkStore() = default;
|
||||
NodeLinkStore(const NodeLinkStore&) = default;
|
||||
NodeLinkStore(NodeLinkStore&&) = default;
|
||||
NodeLinkStore& operator=(const NodeLinkStore&) = default;
|
||||
NodeLinkStore& operator=(NodeLinkStore&&) = default;
|
||||
|
||||
template <typename Ar>
|
||||
Ar& serialize(Ar& ar) {
|
||||
ar(links_);
|
||||
return ar;
|
||||
}
|
||||
|
||||
void AddLink(Link&& lk) noexcept {
|
||||
links_.push_back(std::move(lk));
|
||||
}
|
||||
void RemoveLink(const Link& lk) noexcept {
|
||||
links_.erase(std::remove(links_.begin(), links_.end(), lk), links_.end());
|
||||
}
|
||||
|
||||
inline std::unique_ptr<History::Command> CreateCommandToRemoveExpired(
|
||||
uint64_t id, std::span<const std::string> in, std::span<const std::string> out) noexcept;
|
||||
|
||||
std::span<const Link> items() const noexcept { return links_; }
|
||||
|
||||
private:
|
||||
std::vector<Link> links_;
|
||||
};
|
||||
|
||||
class NodeLinkStore::SwapCommand : public History::Command {
|
||||
public:
|
||||
static std::unique_ptr<SwapCommand> CreateToAdd(
|
||||
NodeLinkStore& target, Link&& lk) noexcept {
|
||||
return std::make_unique<SwapCommand>(target, std::move(lk), false);
|
||||
}
|
||||
static std::unique_ptr<SwapCommand> CreateToRemove(
|
||||
NodeLinkStore& target, Link&& lk) noexcept {
|
||||
return std::make_unique<SwapCommand>(target, std::move(lk), true);
|
||||
}
|
||||
|
||||
SwapCommand(NodeLinkStore& target, Link&& lk, bool added) noexcept :
|
||||
target_(&target), link_(std::move(lk)), added_(added) {
|
||||
}
|
||||
|
||||
void Apply() noexcept override { Exec(); }
|
||||
void Revert() noexcept override { Exec(); }
|
||||
|
||||
private:
|
||||
NodeLinkStore* const target_;
|
||||
Link link_;
|
||||
bool added_;
|
||||
|
||||
void Exec() noexcept {
|
||||
added_?
|
||||
target_->RemoveLink(link_):
|
||||
target_->AddLink(Link(link_));
|
||||
added_ = !added_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::unique_ptr<History::Command> NodeLinkStore::CreateCommandToRemoveExpired(
|
||||
uint64_t id, std::span<const std::string> in, std::span<const std::string> out) noexcept {
|
||||
std::vector<std::unique_ptr<History::Command>> cmds;
|
||||
for (const auto& lk : links_) {
|
||||
const bool rm =
|
||||
(lk.src_id == id && std::find(in .begin(), in .end(), lk.src_name) < in .end()) ||
|
||||
(lk.dst_id == id && std::find(out.begin(), out.end(), lk.dst_name) < out.end());
|
||||
if (rm) cmds.push_back(SwapCommand::CreateToRemove(*this, Link(lk)));
|
||||
}
|
||||
if (cmds.empty()) return nullptr;
|
||||
return std::make_unique<AggregateCommand<History::Command>>(std::move(cmds));
|
||||
}
|
||||
|
||||
} // namespace nf7
|
126
common/value.hh
Normal file
126
common/value.hh
Normal file
@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/string_view.hpp>
|
||||
#include <yas/types/utility/usertype.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/yas.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Value final {
|
||||
public:
|
||||
using IncompatibleException = std::bad_variant_access;
|
||||
|
||||
class Pulse final { };
|
||||
class Data;
|
||||
|
||||
using Boolean = bool;
|
||||
using Integer = int64_t;
|
||||
using Scalar = double;
|
||||
using String = std::string;
|
||||
using DataPtr = std::shared_ptr<Data>;
|
||||
|
||||
Value() noexcept {
|
||||
}
|
||||
Value(const Value&) = default;
|
||||
Value(Value&&) = default;
|
||||
Value& operator=(const Value&) = default;
|
||||
Value& operator=(Value&&) = default;
|
||||
|
||||
bool isPulse() const noexcept { return std::holds_alternative<Pulse>(value_); }
|
||||
bool isBoolean() const noexcept { return std::holds_alternative<Boolean>(value_); }
|
||||
bool isInteger() const noexcept { return std::holds_alternative<Integer>(value_); }
|
||||
bool isScalar() const noexcept { return std::holds_alternative<Scalar>(value_); }
|
||||
bool isString() const noexcept { return std::holds_alternative<String>(value_); }
|
||||
bool isData() const noexcept { return std::holds_alternative<DataPtr>(value_); }
|
||||
|
||||
Integer integer() const { return std::get<Integer>(value_); }
|
||||
Boolean boolean() const { return std::get<Boolean>(value_); }
|
||||
Scalar scalar() const { return std::get<Scalar>(value_); }
|
||||
const String& string() const { return std::get<String>(value_); }
|
||||
const DataPtr& data() const { return std::get<DataPtr>(value_); }
|
||||
|
||||
const char* typeName() const noexcept {
|
||||
struct Visitor final {
|
||||
public:
|
||||
auto operator()(Pulse) noexcept { return "pulse"; }
|
||||
auto operator()(Boolean) noexcept { return "boolean"; }
|
||||
auto operator()(Integer) noexcept { return "integer"; }
|
||||
auto operator()(Scalar) noexcept { return "scalar"; }
|
||||
auto operator()(String) noexcept { return "string"; }
|
||||
auto operator()(DataPtr) noexcept { return "data"; }
|
||||
};
|
||||
return std::visit(Visitor{}, value_);
|
||||
}
|
||||
|
||||
template <typename Ar>
|
||||
Ar& serialize(Ar& ar) noexcept {
|
||||
ar & value_;
|
||||
return ar;
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<Pulse, Boolean, Integer, Scalar, String, DataPtr> value_;
|
||||
};
|
||||
|
||||
class Value::Data {
|
||||
public:
|
||||
Data() = default;
|
||||
virtual ~Data() = default;
|
||||
Data(const Data&) = default;
|
||||
Data(Data&&) = default;
|
||||
Data& operator=(const Data&) = default;
|
||||
Data& operator=(Data&&) = default;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
||||
|
||||
|
||||
namespace yas::detail {
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
nf7::Value::Pulse> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const nf7::Value::Pulse&) {
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, nf7::Value::Pulse&) {
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
nf7::Value::DataPtr> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const nf7::Value::DataPtr&) {
|
||||
throw nf7::Exception("cannot serialize Value::DataPtr");
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, nf7::Value::DataPtr&) {
|
||||
throw nf7::DeserializeException("cannot deserialize Value::DataPtr");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace yas::detail
|
607
file/node_network.cc
Normal file
607
file/node_network.cc
Normal file
@ -0,0 +1,607 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <ImNodes.h>
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/vector.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_history.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/gui_window.hh"
|
||||
#include "common/lambda.hh"
|
||||
#include "common/memento.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/node_adaptor.hh"
|
||||
#include "common/node_link_store.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/yas.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Network final : public nf7::File,
|
||||
public nf7::DirItem,
|
||||
public nf7::Lambda,
|
||||
public nf7::Node {
|
||||
public:
|
||||
static inline const GenericTypeInfo<Network> kType = {"Node/Network", {"DirItem", "Lambda"}};
|
||||
|
||||
class Item;
|
||||
class ExecutionContext;
|
||||
class DebugContext;
|
||||
class CustomEditor;
|
||||
|
||||
using ItemId = uint64_t;
|
||||
using ItemList = std::vector<std::unique_ptr<Item>>;
|
||||
|
||||
Network(Env& env,
|
||||
const gui::Window* win = nullptr,
|
||||
ItemList&& items = {},
|
||||
NodeLinkStore&& links = {}) noexcept :
|
||||
File(kType, env), DirItem(DirItem::kMenu | DirItem::kTooltip),
|
||||
history_(env), win_(*this, "Editor Node/Network", win),
|
||||
items_(std::move(items)), links_(std::move(links)) {
|
||||
Initialize();
|
||||
win_.shown() = true;
|
||||
}
|
||||
|
||||
Network(Env& env, Deserializer& ar) : Network(env) {
|
||||
ar(win_, items_, links_);
|
||||
Initialize();
|
||||
}
|
||||
void Serialize(Serializer& ar) const noexcept override {
|
||||
ar(win_, items_, links_);
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
ItemList items;
|
||||
items.reserve(items_.size());
|
||||
for (const auto& item : items_) {
|
||||
items.push_back(std::make_unique<Item>(env, *item));
|
||||
}
|
||||
return std::make_unique<Network>(
|
||||
env, &win_, std::move(items), NodeLinkStore(links_));
|
||||
}
|
||||
|
||||
File* Find(std::string_view name) const noexcept;
|
||||
|
||||
void Update() noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
void UpdateNode(Node::Editor&) noexcept override;
|
||||
void Handle(const Event& ev) noexcept override;
|
||||
|
||||
std::shared_ptr<nf7::Lambda::Context> CreateContext() noexcept override;
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<nf7::DirItem>(t).Select(this);
|
||||
}
|
||||
|
||||
private:
|
||||
ItemId next_ = 1;
|
||||
|
||||
std::vector<std::unique_ptr<History::Command>> cmdq_;
|
||||
nf7::GenericHistory<History::Command> history_;
|
||||
|
||||
std::unordered_map<ItemId, Item*> item_map_;
|
||||
std::unordered_map<const Node*, Item*> node_map_;
|
||||
|
||||
std::shared_ptr<DebugContext> dctx_;
|
||||
|
||||
// persistent params
|
||||
gui::Window win_;
|
||||
std::vector<std::unique_ptr<Item>> items_;
|
||||
NodeLinkStore links_;
|
||||
ImNodes::CanvasState canvas_;
|
||||
|
||||
|
||||
void Initialize();
|
||||
|
||||
void QueueCommand(const std::shared_ptr<nf7::Context>& ctx,
|
||||
std::unique_ptr<History::Command>&& cmd) noexcept {
|
||||
auto ptr = cmd.get();
|
||||
cmdq_.push_back(std::move(cmd));
|
||||
env().ExecMain(ctx, [ptr]() { ptr->Apply(); });
|
||||
}
|
||||
void QueueCommandSilently(std::unique_ptr<History::Command>&& cmd) noexcept {
|
||||
cmdq_.push_back(std::move(cmd));
|
||||
}
|
||||
|
||||
Item& GetItem(ItemId id) const {
|
||||
auto itr = item_map_.find(id);
|
||||
if (itr == item_map_.end()) {
|
||||
throw Exception("missing item ("+std::to_string(id)+")");
|
||||
}
|
||||
return *itr->second;
|
||||
}
|
||||
Item& GetItem(const Node& node) const {
|
||||
auto itr = node_map_.find(&node);
|
||||
if (itr == node_map_.end()) {
|
||||
throw Exception("missing item");
|
||||
}
|
||||
return *itr->second;
|
||||
}
|
||||
};
|
||||
|
||||
class Network::Item final {
|
||||
public:
|
||||
class Watcher;
|
||||
|
||||
Item(ItemId id, std::unique_ptr<nf7::File>&& file) :
|
||||
id_(id), file_(std::move(file)) {
|
||||
Initialize();
|
||||
}
|
||||
Item(Env& env, const Item& src) noexcept :
|
||||
id_(src.id_), file_(src.file_->Clone(env)) {
|
||||
Initialize();
|
||||
}
|
||||
Item(Item&&) = delete;
|
||||
Item& operator=(const Item&) = delete;
|
||||
Item& operator=(Item&&) = delete;
|
||||
|
||||
Item(Deserializer& ar)
|
||||
try {
|
||||
ar(id_, file_);
|
||||
Initialize();
|
||||
} catch (std::exception&) {
|
||||
throw DeserializeException("failed to deserialize Node/Network item");
|
||||
}
|
||||
void Serialize(Serializer& ar) {
|
||||
ar(id_, file_);
|
||||
}
|
||||
|
||||
void Attach(Network& owner) noexcept;
|
||||
void Detach() noexcept;
|
||||
|
||||
void Update() noexcept {
|
||||
assert(owner_);
|
||||
file_->Update();
|
||||
}
|
||||
void UpdateNode(Node::Editor& ed) noexcept {
|
||||
assert(owner_);
|
||||
node_->UpdateNode(ed);
|
||||
}
|
||||
|
||||
ItemId id() const noexcept { return id_; }
|
||||
File::Id fileId() const noexcept { return file_->id(); }
|
||||
|
||||
nf7::Env& env() const noexcept { return file_->env(); }
|
||||
nf7::File& file() const noexcept { return *file_; }
|
||||
nf7::Lambda& lambda() const noexcept { return *lambda_; }
|
||||
nf7::Node& node() const noexcept { return *node_; }
|
||||
|
||||
private:
|
||||
ItemId id_;
|
||||
|
||||
std::unique_ptr<nf7::File> file_;
|
||||
nf7::Lambda* lambda_;
|
||||
nf7::Memento* memento_;
|
||||
nf7::Node* node_;
|
||||
|
||||
Network* owner_ = nullptr;
|
||||
std::unique_ptr<Watcher> watcher_;
|
||||
|
||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
||||
|
||||
std::optional<nf7::NodeAdaptor> node_adaptor_;
|
||||
|
||||
|
||||
void Initialize() {
|
||||
lambda_ = &file_->interfaceOrThrow<nf7::Lambda>();
|
||||
node_ = file_->interface<nf7::Node>();
|
||||
|
||||
if (node_) {
|
||||
memento_ = file_->interface<nf7::Memento>();
|
||||
} else {
|
||||
node_adaptor_.emplace(*file_);
|
||||
node_ = &*node_adaptor_;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Network::Item::Watcher final : public nf7::Env::Watcher {
|
||||
public:
|
||||
Watcher(Item& owner) noexcept : nf7::Env::Watcher(owner.env()), owner_(&owner) {
|
||||
assert(owner.fileId());
|
||||
Watch(owner.fileId());
|
||||
}
|
||||
private:
|
||||
Item* const owner_;
|
||||
|
||||
void Handle(const File::Event& ev) noexcept override {
|
||||
auto& item = *owner_;
|
||||
auto& node = item.node();
|
||||
|
||||
switch (ev.type) {
|
||||
case File::Event::kUpdate:
|
||||
if (item.memento_) {
|
||||
auto ptag = item.tag_;
|
||||
item.tag_ = item.memento_->Save();
|
||||
if (ptag == item.tag_) return;
|
||||
|
||||
if (item.owner_) {
|
||||
auto& net = *item.owner_;
|
||||
|
||||
// check expired sockets
|
||||
if (auto cmd = net.links_.CreateCommandToRemoveExpired(item.id(), node.input(), node.output())) {
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(net.env(), net.id());
|
||||
ctx->description() = "removing links of expired sockets";
|
||||
net.QueueCommand(ctx, std::move(cmd));
|
||||
}
|
||||
|
||||
// tag change history
|
||||
net.QueueCommandSilently(
|
||||
std::make_unique<Memento::RestoreCommand>(*owner_->memento_, ptag));
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case File::Event::kReqFocus:
|
||||
// TODO
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Network::ExecutionContext : public nf7::Lambda::Context {
|
||||
public:
|
||||
ExecutionContext(Network& owner,
|
||||
const std::shared_ptr<nf7::Lambda::Context>& octx = nullptr) noexcept :
|
||||
Context(owner.env(), owner.id(), octx), owner_(&owner), octx_(octx) {
|
||||
std::unordered_map<ItemId, std::shared_ptr<nf7::Lambda::Context>> ctxmap;
|
||||
subs_.reserve(owner_->items_.size());
|
||||
for (const auto& item : owner_->items_) {
|
||||
auto ctx = item->lambda().CreateContext();
|
||||
subs_[ctx.get()] = SubContext {.ctx = ctx};
|
||||
ctxmap[item->id()] = ctx;
|
||||
}
|
||||
for (const auto& lk : owner_->links_.items()) {
|
||||
try {
|
||||
const auto& src_ctx = ctxmap[lk.src_id];
|
||||
const auto& dst_ctx = ctxmap[lk.dst_id];
|
||||
if (!src_ctx || !dst_ctx) continue;
|
||||
|
||||
const auto src_idx = owner_->GetItem(lk.src_id).node().output(lk.src_name);
|
||||
const auto dst_idx = owner_->GetItem(lk.dst_id).node().input(lk.dst_name);
|
||||
subs_[src_ctx.get()].dsts.emplace(src_idx, std::make_pair(dst_ctx, dst_idx));
|
||||
} catch (Exception&) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Initialize(const std::shared_ptr<nf7::Lambda::Context>& self,
|
||||
const std::shared_ptr<nf7::Lambda::Context>& parent) override {
|
||||
for (auto item : subs_) {
|
||||
item.second.ctx->Initialize(item.second.ctx, parent);
|
||||
}
|
||||
nf7::Lambda::Context::Initialize(self, parent);
|
||||
}
|
||||
void Handle(size_t idx, Value&& v, const std::shared_ptr<nf7::Lambda::Context>& sender) override {
|
||||
// TODO use task queue
|
||||
if (sender == octx_) {
|
||||
// TODO: input from outer
|
||||
return;
|
||||
}
|
||||
auto itr = subs_.find(sender.get());
|
||||
if (itr == subs_.end()) return;
|
||||
|
||||
auto [begin, end] = itr->second.dsts.equal_range(idx);
|
||||
for (auto itr = begin; itr != end; ++itr) {
|
||||
const auto& p = itr->second;
|
||||
Send(p.first, p.second, Value(v));
|
||||
}
|
||||
}
|
||||
|
||||
void CleanUp() noexcept override {
|
||||
}
|
||||
void Abort() noexcept override {
|
||||
}
|
||||
|
||||
size_t GetMemoryUsage() const noexcept override {
|
||||
return 0;
|
||||
}
|
||||
std::string GetDescription() const noexcept override {
|
||||
return "executing Node/Network";
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Send(const std::shared_ptr<nf7::Lambda::Context>& target,
|
||||
size_t idx, Value&& v) noexcept {
|
||||
target->Handle(idx, std::move(v), self());
|
||||
}
|
||||
|
||||
private:
|
||||
Network* const owner_;
|
||||
|
||||
std::shared_ptr<nf7::Lambda::Context> octx_;
|
||||
|
||||
struct SubContext final {
|
||||
public:
|
||||
std::shared_ptr<nf7::Lambda::Context> ctx;
|
||||
std::unordered_multimap<
|
||||
size_t, std::pair<std::shared_ptr<nf7::Lambda::Context>, size_t>> dsts {};
|
||||
};
|
||||
std::unordered_map<nf7::Lambda::Context*, SubContext> subs_;
|
||||
};
|
||||
|
||||
class Network::DebugContext final : public Network::ExecutionContext {
|
||||
public:
|
||||
using ExecutionContext::ExecutionContext;
|
||||
|
||||
std::string GetDescription() const noexcept override {
|
||||
return "executing Node/Network in debug mode";
|
||||
}
|
||||
};
|
||||
|
||||
class Network::CustomEditor final : public nf7::Node::Editor {
|
||||
public:
|
||||
CustomEditor(Network& owner) noexcept : owner_(&owner) {
|
||||
}
|
||||
|
||||
void AddLink(Node& src_node, std::string_view src_name,
|
||||
Node& dst_node, std::string_view dst_name) noexcept override
|
||||
try {
|
||||
auto lk = NodeLinkStore::Link {
|
||||
.src_id = owner_->GetItem(src_node).id(),
|
||||
.src_name = std::string {src_name},
|
||||
.dst_id = owner_->GetItem(dst_node).id(),
|
||||
.dst_name = std::string {dst_name},
|
||||
};
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(owner_->env(), owner_->id());
|
||||
ctx->description() = "adding links between Nodes on Node/Network";
|
||||
owner_->QueueCommand(
|
||||
ctx, NodeLinkStore::SwapCommand::CreateToAdd(owner_->links_, std::move(lk)));
|
||||
} catch (Exception&) {
|
||||
}
|
||||
void RemoveLink(Node& src_node, std::string_view src_name,
|
||||
Node& dst_node, std::string_view dst_name) noexcept override
|
||||
try {
|
||||
auto lk = NodeLinkStore::Link {
|
||||
.src_id = owner_->GetItem(src_node).id(),
|
||||
.src_name = std::string {src_name},
|
||||
.dst_id = owner_->GetItem(dst_node).id(),
|
||||
.dst_name = std::string {dst_name},
|
||||
};
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(owner_->env(), owner_->id());
|
||||
ctx->description() = "removing links between Nodes on Node/Network";
|
||||
owner_->QueueCommand(
|
||||
ctx, NodeLinkStore::SwapCommand::CreateToRemove(owner_->links_, std::move(lk)));
|
||||
} catch (Exception&) {
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<Node*, std::string>> GetSrcOf(
|
||||
Node& dst_node, std::string_view dst_name) const noexcept override
|
||||
try {
|
||||
const auto dst_id = owner_->GetItem(dst_node).id();
|
||||
|
||||
std::vector<std::pair<Node*, std::string>> ret;
|
||||
for (const auto& lk : owner_->links_.items()) {
|
||||
if (lk.dst_id != dst_id || lk.dst_name != dst_name) continue;
|
||||
try {
|
||||
ret.emplace_back(&owner_->GetItem(lk.src_id).node(), lk.src_name);
|
||||
} catch (Exception&) {
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} catch (Exception&) {
|
||||
return {};
|
||||
}
|
||||
std::vector<std::pair<Node*, std::string>> GetDstOf(
|
||||
Node& src_node, std::string_view src_name) const noexcept
|
||||
try {
|
||||
const auto src_id = owner_->GetItem(src_node).id();
|
||||
|
||||
std::vector<std::pair<Node*, std::string>> ret;
|
||||
for (const auto& lk : owner_->links_.items()) {
|
||||
if (lk.src_id != src_id || lk.src_name != src_name) continue;
|
||||
try {
|
||||
ret.emplace_back(&owner_->GetItem(lk.dst_id).node(), lk.dst_name);
|
||||
} catch (Exception&) {
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} catch (Exception&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Lambda::Context> ctx() noexcept override {
|
||||
if (!owner_->dctx_) {
|
||||
owner_->dctx_ = std::make_shared<DebugContext>(*owner_);
|
||||
}
|
||||
return owner_->dctx_;
|
||||
}
|
||||
|
||||
private:
|
||||
Network* const owner_;
|
||||
};
|
||||
|
||||
|
||||
void Network::Initialize() {
|
||||
std::unordered_set<ItemId> ids;
|
||||
for (const auto& item : items_) {
|
||||
const auto id = item->id();
|
||||
if (id == 0) throw Exception("id 0 is invalid");
|
||||
if (ids.contains(id)) throw Exception("id duplication");
|
||||
ids.insert(id);
|
||||
next_ = std::max(next_, id+1);
|
||||
}
|
||||
|
||||
// TODO: remove expired links
|
||||
}
|
||||
File* Network::Find(std::string_view name) const noexcept
|
||||
try {
|
||||
size_t idx;
|
||||
const auto id = std::stol(std::string(name), &idx);
|
||||
if (idx < name.size()) return nullptr;
|
||||
if (id <= 0) return nullptr;
|
||||
return &GetItem(static_cast<ItemId>(id)).file();
|
||||
} catch (std::exception&) {
|
||||
return nullptr;
|
||||
} catch (Exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<nf7::Lambda::Context> Network::CreateContext() noexcept {
|
||||
return std::make_shared<ExecutionContext>(*this);
|
||||
}
|
||||
void Network::Handle(const Event& ev) noexcept {
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
for (const auto& item : items_) item->Attach(*this);
|
||||
break;
|
||||
case Event::kRemove:
|
||||
for (const auto& item : items_) item->Detach();
|
||||
break;
|
||||
case Event::kReqFocus:
|
||||
win_.SetFocus();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Network::Item::Attach(Network& owner) noexcept {
|
||||
assert(owner_);
|
||||
owner_= &owner;
|
||||
|
||||
auto [item_itr, item_inserted] = owner_->item_map_.emplace(id_, this);
|
||||
assert(item_inserted);
|
||||
|
||||
auto [node_itr, node_inserted] = owner_->node_map_.emplace(node_, this);
|
||||
assert(node_inserted);
|
||||
|
||||
file_->MoveUnder(owner, std::to_string(id_));
|
||||
watcher_ = std::make_unique<Watcher>(*this);
|
||||
watcher_->Watch(file_->id());
|
||||
}
|
||||
void Network::Item::Detach() noexcept {
|
||||
assert(!owner_);
|
||||
owner_->item_map_.erase(id_);
|
||||
owner_->node_map_.erase(node_);
|
||||
|
||||
owner_ = nullptr;
|
||||
watcher_ = nullptr;
|
||||
file_->Isolate();
|
||||
}
|
||||
|
||||
|
||||
void Network::Update() noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
// update children
|
||||
for (const auto& item : items_) {
|
||||
item->Update();
|
||||
}
|
||||
|
||||
// editor window
|
||||
const auto kInit = [em]() {
|
||||
ImGui::SetNextWindowSize({36*em, 36*em}, ImGuiCond_FirstUseEver);
|
||||
};
|
||||
if (win_.Begin(kInit)) {
|
||||
ImNodes::BeginCanvas(&canvas_);
|
||||
|
||||
// update child nodes
|
||||
auto ed = CustomEditor {*this};
|
||||
for (const auto& item : items_) {
|
||||
item->UpdateNode(ed);
|
||||
}
|
||||
|
||||
// handle existing links
|
||||
for (const auto& lk : links_.items()) {
|
||||
const auto src_id = reinterpret_cast<void*>(lk.src_id);
|
||||
const auto dst_id = reinterpret_cast<void*>(lk.dst_id);
|
||||
if (!ImNodes::Connection(src_id, lk.src_name.c_str(), dst_id, lk.dst_name.c_str())) {
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(env(), id());
|
||||
auto cmd = NodeLinkStore::SwapCommand::CreateToRemove(links_, NodeLinkStore::Link(lk));
|
||||
ctx->description() = "removing links";
|
||||
QueueCommand(ctx, std::move(cmd));
|
||||
}
|
||||
}
|
||||
|
||||
// handle new link
|
||||
void* src_ptr;
|
||||
const char* src_name;
|
||||
void* dst_ptr;
|
||||
const char* dst_name;
|
||||
if (ImNodes::GetNewConnection(&dst_ptr, &dst_name, &src_ptr, &src_name)) {
|
||||
auto lk = NodeLinkStore::Link {
|
||||
.src_id = reinterpret_cast<ItemId>(src_ptr),
|
||||
.src_name = src_name,
|
||||
.dst_id = reinterpret_cast<ItemId>(dst_ptr),
|
||||
.dst_name = dst_name,
|
||||
};
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(env(), id());
|
||||
auto cmd = NodeLinkStore::SwapCommand::CreateToAdd(links_, std::move(lk));
|
||||
ctx->description() = "adding new link";
|
||||
QueueCommand(ctx, std::move(cmd));
|
||||
}
|
||||
|
||||
ImNodes::EndCanvas();
|
||||
|
||||
// popup menu for canvas
|
||||
constexpr auto kFlags =
|
||||
ImGuiPopupFlags_MouseButtonRight |
|
||||
ImGuiPopupFlags_NoOpenOverExistingPopup;
|
||||
if (ImGui::BeginPopupContextWindow(nullptr, kFlags)) {
|
||||
// TODO
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
win_.End();
|
||||
|
||||
// push queued commands
|
||||
if (cmdq_.size() > 0) {
|
||||
history_.Add(std::make_unique<
|
||||
nf7::AggregateCommand<History::Command>>(std::move(cmdq_)));
|
||||
}
|
||||
}
|
||||
void Network::UpdateMenu() noexcept {
|
||||
ImGui::MenuItem("shown", nullptr, &win_.shown());
|
||||
}
|
||||
void Network::UpdateTooltip() noexcept {
|
||||
ImGui::Text("nodes active: %zu", items_.size());
|
||||
}
|
||||
void Network::UpdateNode(Node::Editor&) noexcept {
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
||||
|
||||
|
||||
namespace yas::detail {
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
std::unique_ptr<nf7::Network::Item>> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const std::unique_ptr<nf7::Network::Item>& item) {
|
||||
item->Serialize(ar);
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, std::unique_ptr<nf7::Network::Item>& item) {
|
||||
item = std::make_unique<nf7::Network::Item>(nf7::Env::Peek(), ar);
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace yas::detail
|
Loading…
Reference in New Issue
Block a user