add Node/ZipTie
This commit is contained in:
parent
06400d4ea4
commit
3c2ed1731a
@ -151,6 +151,7 @@ target_sources(nf7
|
|||||||
file/node_network.cc
|
file/node_network.cc
|
||||||
file/node_ref.cc
|
file/node_ref.cc
|
||||||
file/node_singleton.cc
|
file/node_singleton.cc
|
||||||
|
file/node_ziptie.cc
|
||||||
file/sequencer_adaptor.cc
|
file/sequencer_adaptor.cc
|
||||||
file/sequencer_call.cc
|
file/sequencer_call.cc
|
||||||
file/sequencer_timeline.cc
|
file/sequencer_timeline.cc
|
||||||
|
@ -27,6 +27,11 @@ class SquashedHistory : public nf7::GenericHistory {
|
|||||||
if (staged_.size() == 0) {
|
if (staged_.size() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (++tick_ <= 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tick_ = 0;
|
||||||
|
|
||||||
nf7::GenericHistory::Add(
|
nf7::GenericHistory::Add(
|
||||||
std::make_unique<nf7::AggregateCommand>(std::move(staged_)));
|
std::make_unique<nf7::AggregateCommand>(std::move(staged_)));
|
||||||
return true;
|
return true;
|
||||||
@ -48,6 +53,8 @@ class SquashedHistory : public nf7::GenericHistory {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<Command>> staged_;
|
std::vector<std::unique_ptr<Command>> staged_;
|
||||||
|
|
||||||
|
uint8_t tick_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nf7
|
} // namespace nf7
|
||||||
|
541
file/node_ziptie.cc
Normal file
541
file/node_ziptie.cc
Normal file
@ -0,0 +1,541 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_stdlib.h>
|
||||||
|
|
||||||
|
#include <ImNodes.h>
|
||||||
|
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
|
#include <yas/serialize.hpp>
|
||||||
|
#include <yas/types/std/string.hpp>
|
||||||
|
#include <yas/types/std/vector.hpp>
|
||||||
|
|
||||||
|
#include "nf7.hh"
|
||||||
|
|
||||||
|
#include "common/file_base.hh"
|
||||||
|
#include "common/generic_memento.hh"
|
||||||
|
#include "common/generic_type_info.hh"
|
||||||
|
#include "common/gui.hh"
|
||||||
|
#include "common/life.hh"
|
||||||
|
#include "common/node.hh"
|
||||||
|
#include "common/ptr_selector.hh"
|
||||||
|
#include "common/yas_enum.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace nf7 {
|
||||||
|
|
||||||
|
class ZipTie final : public nf7::FileBase, public nf7::Node {
|
||||||
|
public:
|
||||||
|
static inline const nf7::GenericTypeInfo<ZipTie> kType = {
|
||||||
|
"Node/ZipTie", {"nf7::Node",},
|
||||||
|
"[N to 1] or [1 to N] node",
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr size_t kMaxN = 64;
|
||||||
|
static inline const auto kIndexStrings = ([](){
|
||||||
|
std::vector<std::string> ret(kMaxN);
|
||||||
|
for (size_t i = 0; i < kMaxN; ++i) ret[i] = std::to_string(i);
|
||||||
|
return ret;
|
||||||
|
})();
|
||||||
|
|
||||||
|
class Lambda;
|
||||||
|
|
||||||
|
enum Algorithm {
|
||||||
|
// N to 1
|
||||||
|
kAwait,
|
||||||
|
kMakeTuple,
|
||||||
|
kUpdateTuple,
|
||||||
|
|
||||||
|
// 1 to N
|
||||||
|
kOrderedPulse,
|
||||||
|
kExtractTuple,
|
||||||
|
};
|
||||||
|
static bool IsNto1(Algorithm algo) noexcept {
|
||||||
|
return algo == kAwait || algo == kMakeTuple || algo == kUpdateTuple;
|
||||||
|
}
|
||||||
|
static bool IsNameRequired(Algorithm algo) noexcept {
|
||||||
|
return algo == kMakeTuple || algo == kUpdateTuple || algo == kExtractTuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AlgoMeta final {
|
||||||
|
std::string name;
|
||||||
|
std::string desc;
|
||||||
|
};
|
||||||
|
static inline const std::unordered_map<Algorithm, AlgoMeta> kAlgoMetas = {
|
||||||
|
{kAwait, { .name = "await", .desc = "awaits for all inputs satisfied" }},
|
||||||
|
{kMakeTuple, { .name = "make tuple", .desc = "emits a tuple when all inputs satisfied" }},
|
||||||
|
{kUpdateTuple, { .name = "update tuple", .desc = "emits a tuple when one input satisfied" }},
|
||||||
|
{kOrderedPulse, { .name = "ordered pulse", .desc = "emits a pulse in order" }},
|
||||||
|
{kExtractTuple, { .name = "extract tuple", .desc = "extracts values from a tuple by thier name" }},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
public:
|
||||||
|
Data() {}
|
||||||
|
|
||||||
|
Algorithm algo = kAwait;
|
||||||
|
std::vector<std::string> names = {"", ""};
|
||||||
|
};
|
||||||
|
|
||||||
|
ZipTie(nf7::Env& env, Data&& d = {}) noexcept :
|
||||||
|
nf7::FileBase(kType, env),
|
||||||
|
nf7::Node(nf7::Node::kCustomNode |
|
||||||
|
nf7::Node::kMenu),
|
||||||
|
life_(*this), mem_(*this, std::move(d)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipTie(nf7::Deserializer& ar) : ZipTie(ar.env()) {
|
||||||
|
ar(mem_.data());
|
||||||
|
}
|
||||||
|
void Serialize(nf7::Serializer& ar) const noexcept override {
|
||||||
|
ar(mem_.data());
|
||||||
|
}
|
||||||
|
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
|
||||||
|
return std::make_unique<ZipTie>(env, Data {mem_.data()});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
|
||||||
|
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept override;
|
||||||
|
nf7::Node::Meta GetMeta() const noexcept override {
|
||||||
|
const auto n = mem_->names.size();
|
||||||
|
|
||||||
|
std::vector<std::string> index(
|
||||||
|
kIndexStrings.begin(),
|
||||||
|
kIndexStrings.begin()+static_cast<intmax_t>(n));
|
||||||
|
if (IsNto1(mem_->algo)) {
|
||||||
|
return {std::move(index), {"out"}};
|
||||||
|
} else {
|
||||||
|
return {{"in"}, std::move(index)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateNode(nf7::Node::Editor&) noexcept override;
|
||||||
|
void UpdateMenu(nf7::Node::Editor&) noexcept override;
|
||||||
|
|
||||||
|
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
|
||||||
|
return nf7::InterfaceSelector<nf7::Memento, nf7::Node>(t).Select(this, &mem_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nf7::Life<ZipTie> life_;
|
||||||
|
nf7::GenericMemento<Data> mem_;
|
||||||
|
|
||||||
|
|
||||||
|
// socket list manipulation
|
||||||
|
void InsertSocket(nf7::Node::Editor&, size_t) noexcept;
|
||||||
|
void RemoveSocket(nf7::Node::Editor&, size_t) noexcept;
|
||||||
|
void MoveLinks(nf7::Node::Editor&, std::string_view before, std::string_view after) noexcept;
|
||||||
|
|
||||||
|
// widgets
|
||||||
|
bool SocketMenu(nf7::Node::Editor&, size_t) noexcept;
|
||||||
|
bool AlgorithmComboItem(Algorithm);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ZipTie::Lambda final : public nf7::Node::Lambda,
|
||||||
|
public std::enable_shared_from_this<Lambda> {
|
||||||
|
public:
|
||||||
|
Lambda(ZipTie& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept :
|
||||||
|
nf7::Node::Lambda(f, parent), f_(f.life_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Handle(const nf7::Node::Lambda::Msg& in) noexcept override
|
||||||
|
try {
|
||||||
|
f_.EnforceAlive();
|
||||||
|
|
||||||
|
const auto& d = f_->mem_.data();
|
||||||
|
if (d.algo != std::exchange(prev_algo_, d.algo)) {
|
||||||
|
values_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNto1(d.algo)) {
|
||||||
|
const auto idx = static_cast<size_t>(std::stoul(in.name));
|
||||||
|
if (idx >= d.names.size()) {
|
||||||
|
throw nf7::Exception {"index overflow"};
|
||||||
|
}
|
||||||
|
values_.resize(d.names.size());
|
||||||
|
values_[idx] = in.value;
|
||||||
|
} else {
|
||||||
|
values_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (d.algo) {
|
||||||
|
case kAwait:
|
||||||
|
Await(in);
|
||||||
|
return;
|
||||||
|
case kMakeTuple:
|
||||||
|
MakeTuple(in, d);
|
||||||
|
return;
|
||||||
|
case kUpdateTuple:
|
||||||
|
UpdateTuple(in, d);
|
||||||
|
return;
|
||||||
|
case kOrderedPulse:
|
||||||
|
OrderedPulse(in, d);
|
||||||
|
return;
|
||||||
|
case kExtractTuple:
|
||||||
|
ExtractTuple(in, d);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (std::invalid_argument&) {
|
||||||
|
} catch (std::out_of_range&) {
|
||||||
|
} catch (nf7::ExpiredException&) {
|
||||||
|
} catch (nf7::Exception&) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nf7::Life<ZipTie>::Ref f_;
|
||||||
|
|
||||||
|
std::optional<Algorithm> prev_algo_;
|
||||||
|
|
||||||
|
std::vector<std::optional<nf7::Value>> values_;
|
||||||
|
|
||||||
|
void Await(const nf7::Node::Lambda::Msg& in) noexcept {
|
||||||
|
if (AllSatisifed()) {
|
||||||
|
in.sender->Handle("out", nf7::Value::Pulse {}, shared_from_this());
|
||||||
|
values_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void MakeTuple(const nf7::Node::Lambda::Msg& in, const Data& d) noexcept {
|
||||||
|
if (AllSatisifed()) {
|
||||||
|
UpdateTuple(in, d);
|
||||||
|
values_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void UpdateTuple(const nf7::Node::Lambda::Msg& in, const Data& d) noexcept {
|
||||||
|
std::vector<nf7::Value::TuplePair> pairs;
|
||||||
|
pairs.reserve(d.names.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < d.names.size(); ++i) {
|
||||||
|
const auto& name = d.names[i];
|
||||||
|
if (name == "" || !values_[i]) continue;
|
||||||
|
pairs.emplace_back(name, *values_[i]);
|
||||||
|
}
|
||||||
|
in.sender->Handle("out", std::move(pairs), shared_from_this());
|
||||||
|
}
|
||||||
|
void OrderedPulse(const nf7::Node::Lambda::Msg& in, const Data& d) noexcept {
|
||||||
|
for (size_t i = 0; i < d.names.size(); ++i) {
|
||||||
|
in.sender->Handle(kIndexStrings[i], nf7::Value::Pulse {}, shared_from_this());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ExtractTuple(const nf7::Node::Lambda::Msg& in, const Data& d) noexcept {
|
||||||
|
for (size_t i = 0; i < d.names.size(); ++i)
|
||||||
|
try {
|
||||||
|
in.sender->Handle(kIndexStrings[i], in.value.tuple(d.names[i]), shared_from_this());
|
||||||
|
} catch (nf7::Exception&) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AllSatisifed() const noexcept {
|
||||||
|
return std::all_of(values_.begin(), values_.end(), [](auto& x) { return !!x; });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::shared_ptr<nf7::Node::Lambda> ZipTie::CreateLambda(
|
||||||
|
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
|
||||||
|
return std::make_shared<Lambda>(*this, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZipTie::InsertSocket(nf7::Node::Editor& ed, size_t idx) noexcept {
|
||||||
|
auto& names = mem_->names;
|
||||||
|
assert(names.size() < kMaxN);
|
||||||
|
assert(idx <= names.size());
|
||||||
|
|
||||||
|
for (size_t i = names.size()-1; i > idx; --i) {
|
||||||
|
MoveLinks(ed, kIndexStrings[i-1], kIndexStrings[i]);
|
||||||
|
}
|
||||||
|
env().ExecMain(nullptr, [&names, idx](){
|
||||||
|
names.insert(names.begin()+static_cast<intmax_t>(idx), std::string {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void ZipTie::RemoveSocket(nf7::Node::Editor& ed, size_t idx) noexcept {
|
||||||
|
auto& names = mem_->names;
|
||||||
|
assert(names.size() >= 1);
|
||||||
|
assert(idx < names.size());
|
||||||
|
|
||||||
|
MoveLinks(ed, kIndexStrings[idx], "");
|
||||||
|
for (size_t i = idx; i < names.size()-1; ++i) {
|
||||||
|
MoveLinks(ed, kIndexStrings[i+1], kIndexStrings[i]);
|
||||||
|
}
|
||||||
|
env().ExecMain(nullptr, [&names, idx](){
|
||||||
|
names.erase(names.begin() + static_cast<intmax_t>(idx));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void ZipTie::MoveLinks(
|
||||||
|
nf7::Node::Editor& ed, std::string_view before, std::string_view after) noexcept {
|
||||||
|
const bool self_src = !IsNto1(mem_->algo);
|
||||||
|
|
||||||
|
const auto others =
|
||||||
|
self_src? ed.GetDstOf(*this, before): ed.GetSrcOf(*this, before);
|
||||||
|
for (const auto& other_ref : others) {
|
||||||
|
using P = std::pair<nf7::Node*, std::string_view>;
|
||||||
|
P self = {this, before};
|
||||||
|
P other = {other_ref.first, other_ref.second};
|
||||||
|
|
||||||
|
// remove existing link
|
||||||
|
{
|
||||||
|
auto src = &self, dst = &other;
|
||||||
|
if (!self_src) std::swap(src, dst);
|
||||||
|
ed.RemoveLink(*src->first, src->second, *dst->first, dst->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add removed link
|
||||||
|
self.second = after;
|
||||||
|
if (after != "") {
|
||||||
|
auto src = &self, dst = &other;
|
||||||
|
if (!self_src) std::swap(src, dst);
|
||||||
|
ed.AddLink(*src->first, src->second, *dst->first, dst->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZipTie::UpdateNode(nf7::Node::Editor& ed) noexcept {
|
||||||
|
const auto em = ImGui::GetFontSize();
|
||||||
|
|
||||||
|
auto meta_itr = kAlgoMetas.find(mem_->algo);
|
||||||
|
assert(meta_itr != kAlgoMetas.end());
|
||||||
|
|
||||||
|
const auto& meta = meta_itr->second;
|
||||||
|
|
||||||
|
bool mod = false;
|
||||||
|
|
||||||
|
ImGui::TextUnformatted("Node/ZipTie");
|
||||||
|
ImGui::SameLine();
|
||||||
|
const auto right_top = ImGui::GetCursorPos();
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
const auto left_top = ImGui::GetCursorPos();
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
// inputs
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
if (IsNto1(mem_->algo)) {
|
||||||
|
for (size_t i = 0; i < mem_->names.size(); ++i) {
|
||||||
|
if (ImNodes::BeginInputSlot(kIndexStrings[i].c_str(), 1)) {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
gui::NodeSocket();
|
||||||
|
ImNodes::EndSlot();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopupContextItem()) {
|
||||||
|
mod |= SocketMenu(ed, i);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ImNodes::BeginInputSlot("in", 1)) {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
gui::NodeSocket();
|
||||||
|
ImNodes::EndSlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
// text input
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
for (size_t i = 0; i < mem_->names.size(); ++i) {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
if (!IsNto1(mem_->algo)) {
|
||||||
|
ImGui::TextUnformatted("->");
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
if (IsNameRequired(mem_->algo)) {
|
||||||
|
ImGui::SetNextItemWidth(6*em);
|
||||||
|
|
||||||
|
const auto id = "##text"+kIndexStrings[i];
|
||||||
|
ImGui::InputText(id.c_str(), &mem_->names[i]);
|
||||||
|
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||||
|
mod = true;
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopupContextItem()) {
|
||||||
|
mod |= SocketMenu(ed, i);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::Text("%zu", i);
|
||||||
|
}
|
||||||
|
if (IsNto1(mem_->algo)) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextUnformatted("->");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
// outputs
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
if (IsNto1(mem_->algo)) {
|
||||||
|
if (ImNodes::BeginOutputSlot("out", 1)) {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
gui::NodeSocket();
|
||||||
|
ImNodes::EndSlot();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < mem_->names.size(); ++i) {
|
||||||
|
if (ImNodes::BeginOutputSlot(kIndexStrings[i].c_str(), 1)) {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
gui::NodeSocket();
|
||||||
|
ImNodes::EndSlot();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopupContextItem()) {
|
||||||
|
mod |= SocketMenu(ed, i);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
ImGui::SameLine();
|
||||||
|
const auto right_bottom = ImGui::GetCursorPos();
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
// algorithm selection
|
||||||
|
ImGui::SetCursorPos(left_top);
|
||||||
|
auto w = std::max(right_bottom.x, right_top.x) - left_top.x;
|
||||||
|
ImGui::Button(meta.name.c_str(), {w, 0});
|
||||||
|
if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonLeft)) {
|
||||||
|
ImGui::TextDisabled("N to 1");
|
||||||
|
mod |= AlgorithmComboItem(kAwait);
|
||||||
|
mod |= AlgorithmComboItem(kMakeTuple);
|
||||||
|
mod |= AlgorithmComboItem(kUpdateTuple);
|
||||||
|
|
||||||
|
ImGui::TextDisabled("1 to N");
|
||||||
|
mod |= AlgorithmComboItem(kOrderedPulse);
|
||||||
|
mod |= AlgorithmComboItem(kExtractTuple);
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("choose algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit changes
|
||||||
|
if (mod) {
|
||||||
|
env().ExecMain(
|
||||||
|
std::make_shared<nf7::GenericContext>(*this, "memento commit"),
|
||||||
|
[this]() { mem_.Commit(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZipTie::UpdateMenu(nf7::Node::Editor&) noexcept {
|
||||||
|
if (ImGui::BeginMenu("config")) {
|
||||||
|
static int n;
|
||||||
|
if (ImGui::IsWindowAppearing()) {
|
||||||
|
n = static_cast<int>(mem_->names.size());
|
||||||
|
}
|
||||||
|
ImGui::PushItemWidth(6*ImGui::GetFontSize());
|
||||||
|
|
||||||
|
ImGui::DragInt("sockets", &n, 0.25f, 1, static_cast<int>(kMaxN));
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||||
|
mem_->names.resize(static_cast<size_t>(n));
|
||||||
|
mem_.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZipTie::SocketMenu(nf7::Node::Editor& ed, size_t i) noexcept {
|
||||||
|
bool mod = false;
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(mem_->names.size() >= kMaxN);
|
||||||
|
if (ImGui::MenuItem("insert before")) {
|
||||||
|
InsertSocket(ed, i);
|
||||||
|
mod = true;
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("insert after")) {
|
||||||
|
InsertSocket(ed, i+1);
|
||||||
|
mod = true;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(mem_->names.size() == 1);
|
||||||
|
if (ImGui::MenuItem("remove")) {
|
||||||
|
RemoveSocket(ed, i);
|
||||||
|
mod = true;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZipTie::AlgorithmComboItem(Algorithm algo) {
|
||||||
|
bool mod = false;
|
||||||
|
|
||||||
|
auto itr = kAlgoMetas.find(algo);
|
||||||
|
assert(itr != kAlgoMetas.end());
|
||||||
|
|
||||||
|
const auto& meta = itr->second;
|
||||||
|
if (ImGui::Selectable(meta.name.c_str(), mem_->algo == algo)) {
|
||||||
|
if (mem_->algo != algo) {
|
||||||
|
mem_->algo = algo;
|
||||||
|
mod = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("%s", meta.desc.c_str());
|
||||||
|
}
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nf7
|
||||||
|
|
||||||
|
|
||||||
|
namespace yas::detail {
|
||||||
|
|
||||||
|
NF7_YAS_DEFINE_ENUM_SERIALIZER(nf7::ZipTie::Algorithm);
|
||||||
|
|
||||||
|
template <size_t F>
|
||||||
|
struct serializer<
|
||||||
|
type_prop::not_a_fundamental,
|
||||||
|
ser_case::use_internal_serializer,
|
||||||
|
F,
|
||||||
|
nf7::ZipTie::Data> {
|
||||||
|
public:
|
||||||
|
template <typename Archive>
|
||||||
|
static Archive& save(Archive& ar, const nf7::ZipTie::Data& d) {
|
||||||
|
ar(d.algo);
|
||||||
|
if (nf7::ZipTie::IsNameRequired(d.algo)) {
|
||||||
|
ar(d.names);
|
||||||
|
} else {
|
||||||
|
ar(d.names.size());
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
}
|
||||||
|
template <typename Archive>
|
||||||
|
static Archive& load(Archive& ar, nf7::ZipTie::Data& d) {
|
||||||
|
ar(d.algo);
|
||||||
|
if (nf7::ZipTie::IsNameRequired(d.algo)) {
|
||||||
|
ar(d.names);
|
||||||
|
} else {
|
||||||
|
size_t n;
|
||||||
|
ar(n);
|
||||||
|
d.names.clear();
|
||||||
|
d.names.resize(n);
|
||||||
|
}
|
||||||
|
if (d.names.size() > nf7::ZipTie::kMaxN) {
|
||||||
|
throw nf7::DeserializeException {"Node/ZipTie maximum socket count exceeded"};
|
||||||
|
}
|
||||||
|
if (d.names.size() == 0) {
|
||||||
|
d.names.resize(1);
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yas::detail
|
Loading…
x
Reference in New Issue
Block a user