add Sequencer/Call
This commit is contained in:
parent
cec2763e0a
commit
540ce3bb0c
@ -51,6 +51,9 @@ target_sources(nf7
|
||||
common/buffer.hh
|
||||
common/dir.hh
|
||||
common/dir_item.hh
|
||||
common/file_base.hh
|
||||
common/file_holder.hh
|
||||
common/file_holder.cc
|
||||
common/file_ref.hh
|
||||
common/future.hh
|
||||
common/generic_context.hh
|
||||
@ -68,6 +71,7 @@ target_sources(nf7
|
||||
common/gui_timeline.cc
|
||||
common/gui_window.hh
|
||||
common/history.hh
|
||||
common/life.hh
|
||||
common/lock.hh
|
||||
common/logger.hh
|
||||
common/logger_ref.hh
|
||||
@ -98,6 +102,7 @@ target_sources(nf7
|
||||
common/yas_nf7.hh
|
||||
common/yas_std_atomic.hh
|
||||
common/yas_std_filesystem.hh
|
||||
common/yas_std_variant.hh
|
||||
|
||||
$<$<PLATFORM_ID:Linux>:common/native_file_unix.cc>
|
||||
|
||||
@ -109,6 +114,7 @@ target_sources(nf7
|
||||
file/node_imm.cc
|
||||
file/node_network.cc
|
||||
file/node_ref.cc
|
||||
file/sequencer_call.cc
|
||||
file/sequencer_timeline.cc
|
||||
file/system_dir.cc
|
||||
file/system_imgui_config.cc
|
||||
|
57
common/file_base.hh
Normal file
57
common/file_base.hh
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class FileBase : public nf7::File {
|
||||
public:
|
||||
class Feature {
|
||||
public:
|
||||
Feature() = default;
|
||||
virtual ~Feature() = default;
|
||||
Feature(const Feature&) = delete;
|
||||
Feature(Feature&&) = delete;
|
||||
Feature& operator=(const Feature&) = delete;
|
||||
Feature& operator=(Feature&&) = delete;
|
||||
|
||||
// Feature* is just for avoiding multi inheritance issues with Env::Watcher
|
||||
virtual nf7::File* Find(std::string_view) const noexcept { return nullptr; }
|
||||
virtual void Handle(const nf7::File::Event&) noexcept { }
|
||||
virtual void Update() noexcept { }
|
||||
};
|
||||
|
||||
FileBase(const nf7::File::TypeInfo& t,
|
||||
nf7::Env& env,
|
||||
std::vector<Feature*>&& feats) noexcept :
|
||||
nf7::File(t, env), feats_(std::move(feats)) {
|
||||
}
|
||||
|
||||
nf7::File* Find(std::string_view name) const noexcept override final {
|
||||
for (auto feat : feats_) {
|
||||
if (auto ret = feat->Find(name)) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void Handle(const nf7::File::Event& ev) noexcept override final {
|
||||
for (auto feat : feats_) {
|
||||
feat->Handle(ev);
|
||||
}
|
||||
}
|
||||
void Update() noexcept override final {
|
||||
for (auto feat : feats_) {
|
||||
feat->Update();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Feature*> feats_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
213
common/file_holder.cc
Normal file
213
common/file_holder.cc
Normal file
@ -0,0 +1,213 @@
|
||||
#include "common/file_holder.hh"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
|
||||
#include "common/generic_context.hh"
|
||||
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
FileHolder::FileHolder(
|
||||
nf7::File& owner, std::string_view id, std::string_view iface, const FileHolder* src) :
|
||||
owner_(&owner), id_(id), popup_config_(*this, iface) {
|
||||
if (src) {
|
||||
if (src->own()) {
|
||||
entity_ = src->file()->Clone(owner.env());
|
||||
} else {
|
||||
entity_ = src->entity_;
|
||||
}
|
||||
// SetUp() will be called by kAdd event
|
||||
}
|
||||
}
|
||||
|
||||
nf7::File* FileHolder::Find(std::string_view name) const noexcept {
|
||||
return name == id_? file_: nullptr;
|
||||
}
|
||||
void FileHolder::Handle(const nf7::File::Event& ev) noexcept {
|
||||
switch (ev.type) {
|
||||
case nf7::File::Event::kAdd:
|
||||
assert(!ready_);
|
||||
ready_ = true;
|
||||
SetUp();
|
||||
break;
|
||||
case nf7::File::Event::kRemove:
|
||||
assert(ready_);
|
||||
TearDown();
|
||||
ready_ = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void FileHolder::Update() noexcept {
|
||||
if (own()) {
|
||||
ImGui::PushID(this);
|
||||
file_->Update();
|
||||
ImGui::PopID();
|
||||
}
|
||||
popup_config_.Update();
|
||||
}
|
||||
|
||||
std::string FileHolder::GetDisplayText() const noexcept {
|
||||
std::string text;
|
||||
if (own()) {
|
||||
text = "OWN: " + file_->type().name();
|
||||
} else if (empty()) {
|
||||
text = "NULL:";
|
||||
} else {
|
||||
text = "REF: "s + path().Stringify();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
void FileHolder::UpdateButton(bool small) noexcept {
|
||||
ImGui::PushID(this);
|
||||
|
||||
const auto text = GetDisplayText();
|
||||
if (small? ImGui::SmallButton(text.c_str()): ImGui::Button(text.c_str())) {
|
||||
popup_config_.Open();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
void FileHolder::UpdateLabel(const char* name) noexcept {
|
||||
ImGui::PushID(this);
|
||||
|
||||
if (ImGui::Button(GetDisplayText().c_str(), {ImGui::CalcItemWidth(), 0})) {
|
||||
popup_config_.Open();
|
||||
}
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::TextUnformatted(name);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void FileHolder::SetUp() noexcept {
|
||||
if (!ready_ || file_) return;
|
||||
if (own()) {
|
||||
file_->MoveUnder(*owner_, id_);
|
||||
} else if (ref()) {
|
||||
try {
|
||||
file_ = &owner_->ResolveOrThrow(path());
|
||||
} catch (nf7::File::NotFoundException&) {
|
||||
}
|
||||
}
|
||||
if (file_) {
|
||||
watcher_.emplace(file_->env());
|
||||
watcher_->Watch(file_->id());
|
||||
|
||||
watcher_->AddHandler(nf7::File::Event::kRemove, [this](auto&) {
|
||||
file_ = nullptr;
|
||||
});
|
||||
auto mem = file_->interface<nf7::Memento>();
|
||||
if (own() && mem) {
|
||||
watcher_->AddHandler(nf7::File::Event::kUpdate, [this, mem](auto&) {
|
||||
auto ptag = std::exchange(tag_, mem->Save());
|
||||
if (ptag != tag_) {
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
void FileHolder::TearDown() noexcept {
|
||||
if (!ready_) return;
|
||||
if (own()) {
|
||||
file_->Isolate();
|
||||
}
|
||||
file_ = nullptr;
|
||||
watcher_ = std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
FileHolder::Tag::Tag(const Tag& src) noexcept {
|
||||
if (src.target_) {
|
||||
entity_ = src.target_->entity_;
|
||||
tag_ = src.target_->tag_;
|
||||
} else {
|
||||
entity_ = src.entity_;
|
||||
tag_ = src.tag_;
|
||||
}
|
||||
}
|
||||
FileHolder::Tag& FileHolder::Tag::operator=(const Tag& src) noexcept {
|
||||
assert(!src.target_);
|
||||
assert(target_);
|
||||
|
||||
target_->TearDown();
|
||||
target_->entity_ = src.entity_;
|
||||
target_->tag_ = src.tag_;
|
||||
if (target_->tag_) {
|
||||
target_->file()->interfaceOrThrow<nf7::Memento>().Restore(target_->tag_);
|
||||
}
|
||||
target_->SetUp();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void FileHolder::ConfigPopup::Open() noexcept {
|
||||
if (h_->ref()) {
|
||||
type_ = 1;
|
||||
path_ = h_->path().Stringify();
|
||||
} else {
|
||||
type_ = 0;
|
||||
path_ = "";
|
||||
}
|
||||
nf7::gui::Popup::Open();
|
||||
}
|
||||
void FileHolder::ConfigPopup::Update() noexcept {
|
||||
ImGui::PushID(this);
|
||||
|
||||
auto& owner = *h_->owner_;
|
||||
|
||||
if (Begin()) {
|
||||
if (ImGui::RadioButton("own", type_ == 0)) { type_ = 0; }
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("ref", type_ == 1)) { type_ = 1; }
|
||||
|
||||
if (type_ == 0) {
|
||||
if (factory_.Update(owner)) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
} else if (type_ == 1) {
|
||||
ImGui::InputText("path", &path_);
|
||||
|
||||
bool missing = false;
|
||||
try {
|
||||
auto path = nf7::File::Path::Parse(path_);
|
||||
try {
|
||||
owner.ResolveOrThrow(path);
|
||||
} catch (nf7::File::NotFoundException&) {
|
||||
missing = true;
|
||||
}
|
||||
|
||||
if (ImGui::Button("ok")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(
|
||||
owner, "emplacing reference to file holder '"+h_->id_+"'");
|
||||
owner.env().ExecMain(
|
||||
ctx, [this, p = std::move(path)]() mutable {
|
||||
h_->Emplace(std::move(p));
|
||||
h_->onChange();
|
||||
});
|
||||
}
|
||||
} catch (nf7::Exception& e) {
|
||||
ImGui::Bullet(); ImGui::TextUnformatted(e.msg().c_str());
|
||||
}
|
||||
if (missing) {
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("the file is missing :(");
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
} // namespace nf7
|
156
common/file_holder.hh
Normal file
156
common/file_holder.hh
Normal file
@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/variant.hpp>
|
||||
#include <yas/types/utility/usertype.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/file_base.hh"
|
||||
#include "common/generic_watcher.hh"
|
||||
#include "common/gui_file.hh"
|
||||
#include "common/gui_popup.hh"
|
||||
#include "common/memento.hh"
|
||||
#include "common/yas_nf7.hh"
|
||||
#include "common/yas_std_variant.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class FileHolder : public nf7::FileBase::Feature {
|
||||
public:
|
||||
class Tag;
|
||||
|
||||
class EmptyException final : public nf7::Exception {
|
||||
public:
|
||||
using nf7::Exception::Exception;
|
||||
};
|
||||
|
||||
using Entity = std::variant<
|
||||
std::monostate, nf7::File::Path, std::shared_ptr<nf7::File>>;
|
||||
|
||||
FileHolder(nf7::File& owner, std::string_view id, std::string_view iface,
|
||||
const FileHolder* src = nullptr);
|
||||
FileHolder(const FileHolder&) = delete;
|
||||
FileHolder(FileHolder&&) = delete;
|
||||
FileHolder& operator=(const FileHolder&) = delete;
|
||||
FileHolder& operator=(FileHolder&&) = delete;
|
||||
|
||||
// yas usertype serializer
|
||||
void serialize(auto& ar) {
|
||||
ar(entity_);
|
||||
}
|
||||
|
||||
void Emplace(nf7::File::Path&& path) noexcept {
|
||||
TearDown();
|
||||
entity_ = std::move(path);
|
||||
tag_ = nullptr;
|
||||
SetUp();
|
||||
}
|
||||
void Emplace(std::unique_ptr<nf7::File>&& f) noexcept {
|
||||
TearDown();
|
||||
entity_ = std::move(f);
|
||||
tag_ = nullptr;
|
||||
SetUp();
|
||||
}
|
||||
|
||||
nf7::File& GetFileOrThrow() {
|
||||
SetUp();
|
||||
if (!file_) {
|
||||
throw EmptyException {"holder is empty"};
|
||||
}
|
||||
return *file_;
|
||||
}
|
||||
|
||||
// nf7::FileBase::Feature methods
|
||||
nf7::File* Find(std::string_view name) const noexcept override;
|
||||
void Handle(const nf7::File::Event&) noexcept override;
|
||||
void Update() noexcept override;
|
||||
|
||||
std::string GetDisplayText() const noexcept;
|
||||
void UpdateButton(bool small = false) noexcept;
|
||||
void UpdateLabel(const char* name) noexcept;
|
||||
|
||||
bool own() const noexcept {
|
||||
return std::holds_alternative<std::shared_ptr<nf7::File>>(entity_);
|
||||
}
|
||||
bool ref() const noexcept {
|
||||
return std::holds_alternative<nf7::File::Path>(entity_);
|
||||
}
|
||||
bool empty() const noexcept {
|
||||
return std::holds_alternative<std::monostate>(entity_);
|
||||
}
|
||||
|
||||
nf7::File* file() const noexcept { return file_; }
|
||||
nf7::File::Path path() const noexcept {
|
||||
assert(!empty());
|
||||
return own()? nf7::File::Path {{id_}}: std::get<nf7::File::Path>(entity_);
|
||||
}
|
||||
|
||||
// called when memento of owned file is changed, or target is changed by GUI.
|
||||
// Emplace() doesn't.
|
||||
std::function<void(void)> onChange = [](){};
|
||||
|
||||
private:
|
||||
nf7::File* const owner_;
|
||||
const std::string id_;
|
||||
|
||||
bool ready_ = false; // whether owner is added to file tree
|
||||
|
||||
Entity entity_;
|
||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
||||
|
||||
nf7::File* file_ = nullptr;
|
||||
|
||||
std::optional<nf7::GenericWatcher> watcher_;
|
||||
|
||||
|
||||
// GUI popup
|
||||
struct ConfigPopup final : nf7::gui::Popup {
|
||||
public:
|
||||
ConfigPopup(FileHolder& h, std::string_view iface) noexcept :
|
||||
nf7::gui::Popup("ConfigPopup"), h_(&h), factory_({std::string {iface}}) {
|
||||
}
|
||||
|
||||
void Open() noexcept;
|
||||
void Update() noexcept;
|
||||
|
||||
private:
|
||||
FileHolder* const h_;
|
||||
|
||||
uint32_t type_;
|
||||
std::string path_;
|
||||
nf7::gui::FileFactory<0> factory_;
|
||||
} popup_config_;
|
||||
|
||||
|
||||
void SetUp() noexcept;
|
||||
void TearDown() noexcept;
|
||||
};
|
||||
|
||||
// to save/restore FileHolder's changes through GenericMemento
|
||||
class FileHolder::Tag final {
|
||||
public:
|
||||
Tag(nf7::FileHolder& target) noexcept : target_(&target) {
|
||||
}
|
||||
Tag(const Tag&) noexcept;
|
||||
Tag& operator=(const Tag&) noexcept;
|
||||
Tag(Tag&&) = default;
|
||||
Tag& operator=(Tag&&) = default;
|
||||
|
||||
private:
|
||||
nf7::FileHolder* target_ = nullptr;
|
||||
|
||||
Entity entity_;
|
||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
80
common/life.hh
Normal file
80
common/life.hh
Normal file
@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class LifeExpiredException final : public nf7::Exception {
|
||||
public:
|
||||
using nf7::Exception::Exception;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Life final {
|
||||
public:
|
||||
class Ref;
|
||||
|
||||
Life() = delete;
|
||||
Life(T& target) noexcept : ptr_(&target) {
|
||||
}
|
||||
~Life() noexcept {
|
||||
if (data_) data_->ptr = nullptr;
|
||||
}
|
||||
Life(const Life&) = delete;
|
||||
Life(Life&&) = delete;
|
||||
Life& operator=(const Life&) = delete;
|
||||
Life& operator=(Life&&) = delete;
|
||||
|
||||
private:
|
||||
T* const ptr_;
|
||||
|
||||
struct Data final {
|
||||
T* ptr;
|
||||
};
|
||||
std::shared_ptr<Data> data_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Life<T>::Ref final {
|
||||
public:
|
||||
Ref() = default;
|
||||
Ref(const Life& life) noexcept {
|
||||
if (!life.data_) {
|
||||
auto& l = const_cast<Life&>(life);
|
||||
l.data_ = std::make_shared<Data>();
|
||||
l.data_->ptr = l.ptr_;
|
||||
}
|
||||
data_ = life.data_;
|
||||
}
|
||||
Ref(const Ref&) = default;
|
||||
Ref(Ref&&) = default;
|
||||
Ref& operator=(const Ref&) = default;
|
||||
Ref& operator=(Ref&&) = default;
|
||||
|
||||
void EnforceAlive() const {
|
||||
if (!data_->ptr) {
|
||||
throw LifeExpiredException {"target expired"};
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() noexcept {
|
||||
return !!data_;
|
||||
}
|
||||
T& operator*() noexcept {
|
||||
assert(data_->ptr);
|
||||
return *data_->ptr;
|
||||
}
|
||||
T* operator->() noexcept {
|
||||
return &**this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Data> data_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace nf7
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/value.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
|
@ -33,6 +33,29 @@ struct serializer<
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
std::shared_ptr<nf7::File>> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const std::shared_ptr<nf7::File>& f) {
|
||||
std::unique_ptr<nf7::File> uf(f.get());
|
||||
ar(uf);
|
||||
uf.release();
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, std::shared_ptr<nf7::File>& f) {
|
||||
std::unique_ptr<nf7::File> uf;
|
||||
ar(uf);
|
||||
f = std::move(uf);
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
|
25
common/yas_std_variant.hh
Normal file
25
common/yas_std_variant.hh
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
|
||||
|
||||
namespace yas::detail {
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
std::monostate> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const std::monostate&) {
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, std::monostate&) {
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace yas::detail
|
237
file/sequencer_call.cc
Normal file
237
file/sequencer_call.cc
Normal file
@ -0,0 +1,237 @@
|
||||
#include "nf7.hh"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/vector.hpp>
|
||||
|
||||
#include "common/file_base.hh"
|
||||
#include "common/file_holder.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/sequencer.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Call final : public nf7::FileBase, public nf7::Sequencer {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Call> kType =
|
||||
{"Sequencer/Call", {"Sequencer"}};
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted("Calls a Node.");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted(
|
||||
"implements nf7::Sequencer");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted(
|
||||
"changes will be applied to active lambdas immediately");
|
||||
}
|
||||
|
||||
class Lambda;
|
||||
class SessionLambda;
|
||||
|
||||
Call(Env& env, const nf7::FileHolder* callee = nullptr, std::string_view expects = "") noexcept :
|
||||
FileBase(kType, env, {&callee_}),
|
||||
Sequencer(Sequencer::kCustomItem |
|
||||
Sequencer::kTooltip |
|
||||
Sequencer::kParamPanel),
|
||||
life_(*this),
|
||||
callee_(*this, "callee", "Node", callee),
|
||||
mem_(*this, Data {*this, expects}){
|
||||
callee_.onChange = [this]() {
|
||||
mem_.Commit();
|
||||
};
|
||||
}
|
||||
|
||||
Call(Env& env, Deserializer& ar) : Call(env) {
|
||||
ar(callee_, data().expects, data().pure);
|
||||
}
|
||||
void Serialize(Serializer& ar) const noexcept override {
|
||||
ar(callee_, data().expects, data().pure);
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<Call>(env, &callee_, data().expects);
|
||||
}
|
||||
|
||||
std::shared_ptr<Sequencer::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>&) noexcept override;
|
||||
|
||||
void UpdateItem(Sequencer::Editor&) noexcept override;
|
||||
void UpdateParamPanel(Sequencer::Editor&) noexcept override;
|
||||
void UpdateTooltip(Sequencer::Editor&) noexcept override;
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<
|
||||
nf7::Memento, nf7::Sequencer>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Call> life_;
|
||||
nf7::FileHolder callee_;
|
||||
|
||||
struct Data {
|
||||
Data(Call& f, std::string_view ex) noexcept : callee(f.callee_), expects(ex) {
|
||||
}
|
||||
|
||||
nf7::FileHolder::Tag callee;
|
||||
std::string expects;
|
||||
|
||||
bool pure = false;
|
||||
};
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
|
||||
Data& data() noexcept { return mem_.data(); }
|
||||
const Data& data() const noexcept { return mem_.data(); }
|
||||
};
|
||||
|
||||
|
||||
class Call::Lambda final : public nf7::Sequencer::Lambda,
|
||||
public std::enable_shared_from_this<Call::Lambda> {
|
||||
public:
|
||||
Lambda(Call& f, const std::shared_ptr<nf7::Context>& ctx) noexcept :
|
||||
Sequencer::Lambda(f, ctx), file_(f.life_) {
|
||||
}
|
||||
|
||||
void Run(const std::shared_ptr<Sequencer::Session>& ss) noexcept;
|
||||
|
||||
private:
|
||||
nf7::Life<Call>::Ref file_;
|
||||
|
||||
std::shared_ptr<Call::SessionLambda> ssla_;
|
||||
|
||||
nf7::Node* cached_node_ = nullptr;
|
||||
std::shared_ptr<Node::Lambda> la_;
|
||||
};
|
||||
class Call::SessionLambda final : public nf7::Node::Lambda {
|
||||
public:
|
||||
SessionLambda(Call& f, const std::shared_ptr<Call::Lambda>& parent) noexcept :
|
||||
nf7::Node::Lambda(f, parent) {
|
||||
}
|
||||
~SessionLambda() noexcept {
|
||||
if (ss_ && expects_.size() > 0) {
|
||||
ss_->Finish();
|
||||
}
|
||||
}
|
||||
|
||||
void Listen(Call& f, const std::shared_ptr<Sequencer::Session>& ss) noexcept {
|
||||
assert(!ss_);
|
||||
ss_ = ss;
|
||||
|
||||
const auto ex = f.data().expects;
|
||||
size_t begin = 0;
|
||||
for (size_t i = 0; i <= ex.size(); ++i) {
|
||||
if (i == ex.size() || ex[i] == '\n') {
|
||||
auto name = ex.substr(begin, i-begin);
|
||||
if (name.size() > 0) {
|
||||
expects_.insert(std::move(name));
|
||||
}
|
||||
begin = i+1;
|
||||
}
|
||||
}
|
||||
FinishIf();
|
||||
}
|
||||
void Handle(std::string_view name, const nf7::Value& val,
|
||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override {
|
||||
if (!ss_) return;
|
||||
ss_->Send(name, nf7::Value {val});
|
||||
|
||||
expects_.erase(std::string {name});
|
||||
FinishIf();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Sequencer::Session> ss_;
|
||||
|
||||
std::unordered_set<std::string> expects_;
|
||||
|
||||
void FinishIf() noexcept {
|
||||
if (expects_.size() == 0) {
|
||||
ss_->Finish();
|
||||
ss_ = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
std::shared_ptr<Sequencer::Lambda> Call::CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>& parent) noexcept {
|
||||
return std::make_shared<Call::Lambda>(*this, parent);
|
||||
}
|
||||
void Call::Lambda::Run(const std::shared_ptr<Sequencer::Session>& ss) noexcept
|
||||
try {
|
||||
file_.EnforceAlive();
|
||||
|
||||
auto& data = file_->data();
|
||||
auto& callee = file_->callee_.GetFileOrThrow();
|
||||
auto& node = callee.interfaceOrThrow<nf7::Node>();
|
||||
|
||||
if (!ssla_) {
|
||||
ssla_ = std::make_shared<Call::SessionLambda>(*file_, shared_from_this());
|
||||
}
|
||||
|
||||
auto self = shared_from_this();
|
||||
if (!la_ || &node != std::exchange(cached_node_, &node)) {
|
||||
la_ = node.CreateLambda(ssla_);
|
||||
}
|
||||
|
||||
ssla_->Listen(*file_, ss);
|
||||
for (const auto& name : node.input()) {
|
||||
if (auto v = ss->Receive(name)) {
|
||||
la_->Handle(name, *v, ssla_);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.pure) {
|
||||
ssla_ = nullptr;
|
||||
la_ = nullptr;
|
||||
}
|
||||
} catch (nf7::LifeExpiredException&) {
|
||||
ss->Finish();
|
||||
} catch (nf7::FileHolder::EmptyException&) {
|
||||
ss->Finish();
|
||||
} catch (nf7::File::NotImplementedException&) {
|
||||
ss->Finish();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Call::UpdateItem(Sequencer::Editor&) noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetCursorPos({.25f*em, .25f*em});
|
||||
callee_.UpdateButton(true);
|
||||
}
|
||||
void Call::UpdateParamPanel(Sequencer::Editor&) noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
if (ImGui::CollapsingHeader("Sequencer/Call", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
callee_.UpdateLabel("callee");
|
||||
|
||||
ImGui::InputTextMultiline("expects", &data().expects, {0, 4.f*em});
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
mem_.Commit();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("session ends right after receiving these outputs");
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("pure", &data().pure)) {
|
||||
mem_.Commit();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("callee's lambda is created for each session");
|
||||
}
|
||||
}
|
||||
}
|
||||
void Call::UpdateTooltip(Sequencer::Editor&) noexcept {
|
||||
ImGui::TextUnformatted("Sequencer/Call");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
@ -305,7 +305,7 @@ class TL::Item final : nf7::Env::Watcher {
|
||||
Watcher(f->env()),
|
||||
id_(id), file_(std::move(f)),
|
||||
seq_(&file_->interfaceOrThrow<nf7::Sequencer>()),
|
||||
mem_(f->interface<nf7::Memento>()),
|
||||
mem_(file_->interface<nf7::Memento>()),
|
||||
timing_(t), display_timing_(t) {
|
||||
}
|
||||
Item(const Item&) = delete;
|
||||
@ -648,6 +648,9 @@ class TL::Lambda final : public Node::Lambda,
|
||||
|
||||
void Abort() noexcept {
|
||||
aborted_ = true;
|
||||
for (auto& p : lambdas_) {
|
||||
p.second->Abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool aborted() const noexcept { return aborted_; }
|
||||
@ -1245,6 +1248,12 @@ void TL::Handle(const Event& ev) noexcept {
|
||||
}
|
||||
|
||||
void TL::Update() noexcept {
|
||||
for (const auto& layer : layers_) {
|
||||
for (const auto& item : layer->items()) {
|
||||
item->file().Update();
|
||||
}
|
||||
}
|
||||
|
||||
popup_add_item_.Update();
|
||||
popup_config_.Update();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user