247 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #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/gui.hh"
 | |
| #include "common/generic_context.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", {"nf7::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;
 | |
| 
 | |
|   struct Data {
 | |
|     nf7::File::Path callee;
 | |
|     std::string     expects;
 | |
|     bool            pure;
 | |
| 
 | |
|     void serialize(auto& ar) {
 | |
|       ar(callee, expects, pure);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   Call(nf7::Env& env, Data&& data = {}) noexcept :
 | |
|       nf7::FileBase(kType, env),
 | |
|       Sequencer(Sequencer::kCustomItem |
 | |
|                 Sequencer::kTooltip |
 | |
|                 Sequencer::kParamPanel),
 | |
|       life_(*this),
 | |
|       mem_(*this, std::move(data)) {
 | |
|   }
 | |
| 
 | |
|   Call(nf7::Deserializer& ar) : Call(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<Call>(env, Data {mem_.data()});
 | |
|   }
 | |
| 
 | |
|   std::shared_ptr<nf7::Sequencer::Lambda> CreateLambda(
 | |
|       const std::shared_ptr<nf7::Context>&) noexcept override;
 | |
| 
 | |
|   void UpdateItem(nf7::Sequencer::Editor&) noexcept override;
 | |
|   void UpdateParamPanel(nf7::Sequencer::Editor&) noexcept override;
 | |
|   void UpdateTooltip(nf7::Sequencer::Editor&) noexcept override;
 | |
| 
 | |
|   nf7::File::Interface* interface(const std::type_info& t) noexcept override {
 | |
|     return nf7::InterfaceSelector<
 | |
|         nf7::Memento, nf7::Sequencer>(t).Select(this, &mem_);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   nf7::Life<Call> life_;
 | |
| 
 | |
|   nf7::GenericMemento<Data> mem_;
 | |
| };
 | |
| 
 | |
| 
 | |
| 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 override;
 | |
|   void Abort() noexcept override;
 | |
| 
 | |
|  private:
 | |
|   nf7::Life<Call>::Ref file_;
 | |
| 
 | |
|   std::shared_ptr<Call::SessionLambda> ssla_;
 | |
| 
 | |
|   nf7::Node* cached_node_ = nullptr;
 | |
|   std::shared_ptr<Node::Lambda> la_;
 | |
| 
 | |
|   bool abort_ = false;
 | |
| };
 | |
| 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) {
 | |
|   }
 | |
| 
 | |
|   void Listen(Call& f, const std::shared_ptr<Sequencer::Session>& ss) noexcept {
 | |
|     assert(!ss_);
 | |
|     ss_ = ss;
 | |
| 
 | |
|     const auto ex = f.mem_->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(const nf7::Node::Lambda::Msg& in) noexcept override {
 | |
|     if (!ss_) return;
 | |
|     ss_->Send(in.name, nf7::Value {in.value});
 | |
|     expects_.erase(in.name);
 | |
|     FinishIf();
 | |
|   }
 | |
|   void Abort() noexcept override {
 | |
|     if (ss_) {
 | |
|       ss_->Finish();
 | |
|       ss_ = nullptr;
 | |
|       expects_.clear();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  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 {
 | |
|   if (abort_) return;
 | |
|   file_.EnforceAlive();
 | |
| 
 | |
|   auto& data   = file_->mem_.data();
 | |
|   auto& callee = file_->ResolveOrThrow(data.callee);
 | |
|   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);
 | |
|   const auto inputs = node.GetMeta().inputs;
 | |
|   for (const auto& name : inputs) {
 | |
|     if (auto v = ss->Receive(name)) {
 | |
|       la_->Handle(name, *v, ssla_);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (data.pure) {
 | |
|     ssla_ = nullptr;
 | |
|     la_   = nullptr;
 | |
|   }
 | |
| } catch (nf7::Exception&) {
 | |
|   ss->Finish();
 | |
| }
 | |
| void Call::Lambda::Abort() noexcept {
 | |
|   if (ssla_) {
 | |
|     ssla_->Abort();
 | |
|     ssla_ = nullptr;
 | |
|   }
 | |
|   if (la_) {
 | |
|     la_->Abort();
 | |
|     la_ = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void Call::UpdateItem(Sequencer::Editor&) noexcept {
 | |
|   ImGui::Text("%s", mem_->callee.Stringify().c_str());
 | |
| }
 | |
| void Call::UpdateParamPanel(Sequencer::Editor&) noexcept {
 | |
|   const auto em = ImGui::GetFontSize();
 | |
| 
 | |
|   bool commit = false;
 | |
|   if (ImGui::CollapsingHeader("Sequencer/Call", ImGuiTreeNodeFlags_DefaultOpen)) {
 | |
|     if (nf7::gui::PathButton("callee", mem_->callee, *this)) {
 | |
|       commit = true;
 | |
|     }
 | |
| 
 | |
|     ImGui::InputTextMultiline("expects", &mem_->expects, {0, 4.f*em});
 | |
|     if (ImGui::IsItemDeactivatedAfterEdit()) {
 | |
|       commit = true;
 | |
|     }
 | |
|     if (ImGui::IsItemHovered()) {
 | |
|       ImGui::SetTooltip("session ends right after receiving these outputs");
 | |
|     }
 | |
| 
 | |
|     if (ImGui::Checkbox("pure", &mem_->pure)) {
 | |
|       commit = true;
 | |
|     }
 | |
|     if (ImGui::IsItemHovered()) {
 | |
|       ImGui::SetTooltip("callee's lambda is created for each session");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (commit) {
 | |
|     mem_.Commit();
 | |
|   }
 | |
| }
 | |
| void Call::UpdateTooltip(Sequencer::Editor&) noexcept {
 | |
|   ImGui::TextUnformatted("Sequencer/Call");
 | |
| }
 | |
| 
 | |
| }
 | |
| }  // namespace nf7
 |