replace FileRef to FileHolder
This commit is contained in:
parent
02cc9bf49d
commit
121209161a
@ -54,7 +54,6 @@ target_sources(nf7
|
||||
common/file_base.hh
|
||||
common/file_holder.hh
|
||||
common/file_holder.cc
|
||||
common/file_ref.hh
|
||||
common/future.hh
|
||||
common/generic_context.hh
|
||||
common/generic_history.hh
|
||||
|
@ -111,14 +111,19 @@ FileHolder::Tag::Tag(const Tag& src) noexcept {
|
||||
}
|
||||
}
|
||||
FileHolder::Tag& FileHolder::Tag::operator=(const Tag& src) noexcept {
|
||||
assert(!src.target_);
|
||||
assert(target_);
|
||||
|
||||
target_->TearDown();
|
||||
target_->entity_ = src.entity_;
|
||||
target_->tag_ = src.tag_;
|
||||
target_->SetUp();
|
||||
|
||||
if (!src.target_ && target_) {
|
||||
// restore
|
||||
target_->TearDown();
|
||||
target_->entity_ = src.entity_;
|
||||
target_->tag_ = src.tag_;
|
||||
target_->SetUp();
|
||||
} else if (!src.target_ && !target_) {
|
||||
// copy
|
||||
entity_ = src.entity_;
|
||||
tag_ = src.tag_;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ class FileHolder : public nf7::FileBase::Feature {
|
||||
}
|
||||
|
||||
nf7::File& owner() const noexcept { return *owner_; }
|
||||
const std::string& id() const noexcept { return id_; }
|
||||
|
||||
nf7::File* file() const noexcept { return file_; }
|
||||
nf7::File::Path path() const noexcept {
|
||||
@ -121,6 +122,7 @@ class FileHolder : public nf7::FileBase::Feature {
|
||||
// to save/restore FileHolder's changes through GenericMemento
|
||||
class FileHolder::Tag final {
|
||||
public:
|
||||
Tag() = default;
|
||||
Tag(nf7::FileHolder& target) noexcept : target_(&target) {
|
||||
}
|
||||
Tag(const Tag&) noexcept;
|
||||
|
@ -1,89 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/utility/usertype.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/yas_nf7.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class FileRef {
|
||||
public:
|
||||
FileRef() = delete;
|
||||
FileRef(File& owner) noexcept : owner_(&owner) {
|
||||
}
|
||||
FileRef(File& owner, const File::Path& p, File::Id id = 0) noexcept :
|
||||
owner_(&owner), path_({p}), id_(id) {
|
||||
}
|
||||
FileRef(File& owner, File::Path&& p, File::Id id = 0) noexcept :
|
||||
owner_(&owner), path_(std::move(p)), id_(id) {
|
||||
}
|
||||
FileRef(const FileRef&) = default;
|
||||
FileRef(FileRef&&) = default;
|
||||
FileRef& operator=(const FileRef&) = default;
|
||||
FileRef& operator=(FileRef&&) = default;
|
||||
|
||||
File& operator*() const
|
||||
try {
|
||||
return owner_->env().GetFileOrThrow(id_);
|
||||
} catch (ExpiredException&) {
|
||||
auto& ret = owner_->ResolveOrThrow(path_);
|
||||
const_cast<File::Id&>(id_) = ret.id();
|
||||
return ret;
|
||||
}
|
||||
|
||||
FileRef& operator=(const File::Path& path) noexcept {
|
||||
return operator=(File::Path{path});
|
||||
}
|
||||
FileRef& operator=(File::Path&& path) noexcept {
|
||||
if (path_ != path) {
|
||||
path_ = std::move(path);
|
||||
id_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const File::Path& path() const noexcept { return path_; }
|
||||
File::Id id() const { **this; return id_; }
|
||||
|
||||
private:
|
||||
File* owner_;
|
||||
|
||||
File::Path path_;
|
||||
|
||||
File::Id id_ = 0;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
||||
|
||||
|
||||
|
||||
namespace yas::detail {
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
nf7::FileRef> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const nf7::FileRef& ref) {
|
||||
ar(ref.path());
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, nf7::FileRef& ref) {
|
||||
nf7::File::Path path;
|
||||
ar(path);
|
||||
ref = path;
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace yas::detail
|
@ -35,6 +35,7 @@ class Future final {
|
||||
class Coro;
|
||||
|
||||
using Handle = std::coroutine_handle<Promise>;
|
||||
using Imm = std::variant<T, std::exception_ptr>;
|
||||
|
||||
enum State { kYet, kDone, kError, };
|
||||
|
||||
@ -203,6 +204,10 @@ class Future final {
|
||||
}
|
||||
Future(std::exception_ptr e) noexcept : imm_({e}) {
|
||||
}
|
||||
Future(const Imm& imm) noexcept : imm_(imm) {
|
||||
}
|
||||
Future(Imm&& imm) noexcept : imm_(std::move(imm)) {
|
||||
}
|
||||
Future(const Future&) = default;
|
||||
Future(Future&&) = default;
|
||||
Future& operator=(const Future&) = default;
|
||||
@ -235,7 +240,8 @@ class Future final {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
ctx->env().ExecSub(ctx, std::bind(f, Future(data_)));
|
||||
assert(imm_);
|
||||
ctx->env().ExecSub(ctx, std::bind(f, Future(*imm_)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -304,7 +310,7 @@ class Future final {
|
||||
auto await_resume() { return value(); }
|
||||
|
||||
private:
|
||||
std::optional<std::variant<T, std::exception_ptr>> imm_;
|
||||
std::optional<Imm> imm_;
|
||||
std::shared_ptr<Data> data_;
|
||||
|
||||
Future(const std::shared_ptr<Data>& data) noexcept : data_(data) { }
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/generic_context.hh"
|
||||
|
||||
|
||||
@ -12,6 +13,15 @@ using namespace std::literals;
|
||||
|
||||
namespace nf7::gui {
|
||||
|
||||
static nf7::DirItem* GetDirItem(nf7::FileHolder& h, nf7::DirItem::Flags f) noexcept
|
||||
try {
|
||||
auto& d = h.GetFileOrThrow().interfaceOrThrow<nf7::DirItem>();
|
||||
return d.flags() & f? &d: nullptr;
|
||||
} catch (nf7::Exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool FileFactory::Update() noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
@ -116,41 +126,70 @@ std::string FileHolderEditor::GetDisplayText() const noexcept {
|
||||
return text;
|
||||
}
|
||||
|
||||
void FileHolderEditor::Button(bool small) noexcept {
|
||||
void FileHolderEditor::Button(float w, bool small) noexcept {
|
||||
ImGui::PushID(this);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
const auto text = GetDisplayText();
|
||||
|
||||
const bool open = small?
|
||||
open_ |= small?
|
||||
ImGui::SmallButton(text.c_str()):
|
||||
ImGui::Button(text.c_str());
|
||||
if (open) {
|
||||
ImGui::OpenPopup("FileHolderEditorPopup");
|
||||
ImGui::Button(text.c_str(), {w, 0});
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
MenuItems();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", text.c_str());
|
||||
ImGui::BeginTooltip();
|
||||
Tooltip();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
UpdatePopup();
|
||||
ImGui::PopID();
|
||||
}
|
||||
void FileHolderEditor::ButtonWithLabel(const char* name) noexcept {
|
||||
ImGui::PushID(this);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
if (ImGui::Button(GetDisplayText().c_str(), {ImGui::CalcItemWidth(), 0})) {
|
||||
ImGui::OpenPopup("FileHolderEditorPopup");
|
||||
}
|
||||
Button(ImGui::CalcItemWidth());
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::TextUnformatted(name);
|
||||
ImGui::EndGroup();
|
||||
|
||||
UpdatePopup();
|
||||
ImGui::PopID();
|
||||
}
|
||||
void FileHolderEditor::UpdatePopup() noexcept {
|
||||
void FileHolderEditor::Tooltip() noexcept {
|
||||
ImGui::TextUnformatted(GetDisplayText().c_str());
|
||||
ImGui::Indent();
|
||||
if (auto a = GetDirItem(*holder_, nf7::DirItem::kTooltip)) {
|
||||
a->UpdateTooltip();
|
||||
}
|
||||
ImGui::Unindent();
|
||||
}
|
||||
void FileHolderEditor::MenuItems() noexcept {
|
||||
if (ImGui::MenuItem("emplace")) {
|
||||
open_ = true;
|
||||
}
|
||||
if (auto a = GetDirItem(*holder_, nf7::DirItem::kMenu)) {
|
||||
ImGui::Separator();
|
||||
a->UpdateMenu();
|
||||
}
|
||||
}
|
||||
void FileHolderEditor::MenuWithTooltip(const char* name) noexcept {
|
||||
if (ImGui::BeginMenu(name)) {
|
||||
MenuItems();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
Tooltip();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void FileHolderEditor::Update() noexcept {
|
||||
ImGui::PushID(this);
|
||||
|
||||
if (std::exchange(open_, false)) {
|
||||
ImGui::OpenPopup("FileHolderEditorPopup");
|
||||
}
|
||||
if (ImGui::BeginPopup("FileHolderEditorPopup")) {
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
if (holder_->ref()) {
|
||||
@ -208,6 +247,8 @@ void FileHolderEditor::UpdatePopup() noexcept {
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
} // namespace nf7::gui
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/file_base.hh"
|
||||
#include "common/file_holder.hh"
|
||||
|
||||
|
||||
@ -47,7 +48,7 @@ class FileFactory final {
|
||||
std::string type_filter_;
|
||||
};
|
||||
|
||||
class FileHolderEditor final {
|
||||
class FileHolderEditor final : public nf7::FileBase::Feature {
|
||||
public:
|
||||
enum Type {
|
||||
kOwn,
|
||||
@ -64,18 +65,23 @@ class FileHolderEditor final {
|
||||
|
||||
std::string GetDisplayText() const noexcept;
|
||||
|
||||
void Button(bool = false) noexcept;
|
||||
void SmallButton() noexcept { Button(true); }
|
||||
void Button(float w = 0, bool = false) noexcept;
|
||||
void SmallButton() noexcept { Button(0, true); }
|
||||
void ButtonWithLabel(const char* id) noexcept;
|
||||
void Tooltip() noexcept;
|
||||
void MenuItems() noexcept;
|
||||
void MenuWithTooltip(const char* name) noexcept;
|
||||
|
||||
void Update() noexcept override;
|
||||
|
||||
private:
|
||||
nf7::FileHolder* const holder_;
|
||||
|
||||
bool open_ = false;
|
||||
|
||||
Type type_;
|
||||
FileFactory factory_;
|
||||
std::string path_;
|
||||
|
||||
void UpdatePopup() noexcept;
|
||||
};
|
||||
|
||||
} // namespace nf7::gui
|
||||
|
@ -15,12 +15,13 @@
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_ref.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/file_holder.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_watcher.hh"
|
||||
#include "common/gui_dnd.hh"
|
||||
#include "common/gui_file.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/logger_ref.hh"
|
||||
#include "common/luajit_obj.hh"
|
||||
#include "common/luajit_queue.hh"
|
||||
@ -38,33 +39,39 @@ using namespace std::literals;
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Node final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
public:
|
||||
static inline const GenericTypeInfo<Node> kType =
|
||||
{"LuaJIT/Node", {"nf7::DirItem",}};
|
||||
{"LuaJIT/Node", {"nf7::DirItem"}};
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted("Defines new Node using LuaJIT/Obj.");
|
||||
ImGui::TextUnformatted("Defines new Node using Lua object factory.");
|
||||
ImGui::Bullet();
|
||||
ImGui::TextUnformatted("refers nf7::luajit::Queue through linked LuaJIT/Obj");
|
||||
ImGui::Bullet();
|
||||
ImGui::TextUnformatted("requires nf7::luajit::Obj to refer this Node from externals");
|
||||
}
|
||||
|
||||
class FetchTask;
|
||||
class Lambda;
|
||||
|
||||
struct Data {
|
||||
std::string desc;
|
||||
nf7::FileHolder::Tag obj;
|
||||
std::string desc;
|
||||
std::vector<std::string> inputs;
|
||||
std::vector<std::string> outputs;
|
||||
};
|
||||
|
||||
Node(Env& env, File::Path&& path = {}, Data&& data = {}) noexcept :
|
||||
File(kType, env),
|
||||
DirItem(DirItem::kMenu | DirItem::kTooltip | DirItem::kDragDropTarget),
|
||||
Node(Env& env, const nf7::FileHolder* obj = nullptr, Data&& data = {}) noexcept :
|
||||
nf7::FileBase(kType, env, {&obj_, &obj_editor_}),
|
||||
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kTooltip),
|
||||
life_(*this),
|
||||
log_(std::make_shared<nf7::LoggerRef>()),
|
||||
obj_(*this, std::move(path)),
|
||||
obj_(*this, "obj_factory", obj),
|
||||
obj_editor_(obj_, [](auto& t) { return t.flags().contains("nf7::luajit::Obj"); }),
|
||||
mem_(std::move(data)) {
|
||||
this->data().obj = nf7::FileHolder::Tag {obj_};
|
||||
mem_.CommitAmend();
|
||||
|
||||
obj_.onChildMementoChange = [this]() { mem_.Commit(); };
|
||||
obj_.onEmplace = [this]() { mem_.Commit(); };
|
||||
|
||||
mem_.onRestore = [this]() { Touch(); };
|
||||
mem_.onCommit = [this]() { Touch(); };
|
||||
}
|
||||
@ -80,7 +87,7 @@ class Node final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
ar(obj_, data().desc, data().inputs, data().outputs);
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<Node>(env, File::Path(obj_.path()), Data {data()});
|
||||
return std::make_unique<Node>(env, &obj_, Data {data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
|
||||
@ -97,23 +104,23 @@ class Node final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
static void UpdateList(std::vector<std::string>&) noexcept;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
void UpdateDragDropTarget() noexcept override;
|
||||
void UpdateNode(Node::Editor&) noexcept override;
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<nf7::DirItem, nf7::Node>(t).Select(this);
|
||||
return nf7::InterfaceSelector<
|
||||
nf7::DirItem, nf7::Memento, nf7::Node>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Node> life_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
std::shared_ptr<nf7::luajit::Ref> handler_;
|
||||
nf7::Task<std::shared_ptr<nf7::luajit::Ref>>::Holder fetch_;
|
||||
|
||||
const char* popup_ = nullptr;
|
||||
|
||||
nf7::FileRef obj_;
|
||||
std::optional<nf7::GenericWatcher> watcher_;
|
||||
nf7::FileHolder obj_;
|
||||
nf7::gui::FileHolderEditor obj_editor_;
|
||||
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
const Data& data() const noexcept { return mem_.data(); }
|
||||
@ -122,12 +129,6 @@ class Node final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> FetchHandler() noexcept;
|
||||
|
||||
void DropHandler() noexcept {
|
||||
watcher_ = std::nullopt;
|
||||
handler_ = nullptr;
|
||||
fetch_ = {};
|
||||
}
|
||||
|
||||
static void Uniq(std::vector<std::string>& v) {
|
||||
for (auto itr = v.begin(); itr < v.end();) {
|
||||
if (v.end() != std::find(itr+1, v.end(), *itr)) {
|
||||
@ -154,62 +155,26 @@ class Node final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
}
|
||||
};
|
||||
|
||||
class Node::FetchTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>> {
|
||||
public:
|
||||
FetchTask(Node& target) noexcept :
|
||||
Task(target.env(), target.id()), target_(&target), log_(target_->log_) {
|
||||
}
|
||||
|
||||
private:
|
||||
Node* const target_;
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>>::Coro Proc() noexcept {
|
||||
try {
|
||||
auto& objf = *target_->obj_;
|
||||
auto& obj = objf.interfaceOrThrow<nf7::luajit::Obj>();
|
||||
auto handler = co_await obj.Build();
|
||||
co_yield handler;
|
||||
|
||||
try {
|
||||
*target_->obj_; // checks if objf is alive
|
||||
|
||||
target_->handler_ = handler;
|
||||
|
||||
auto& w = target_->watcher_;
|
||||
w.emplace(env());
|
||||
w->Watch(objf.id());
|
||||
w->AddHandler(Event::kUpdate, [t = target_](auto&) {
|
||||
if (t->handler_) {
|
||||
t->log_->Info("detected update of handler object, drops cache");
|
||||
t->handler_ = nullptr;
|
||||
}
|
||||
});
|
||||
} catch (Exception& e) {
|
||||
log_->Error("watcher setup failure: "+e.msg());
|
||||
}
|
||||
} catch (Exception& e) {
|
||||
log_->Error("fetch failure: "+e.msg());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Node::Lambda final : public nf7::Node::Lambda,
|
||||
public std::enable_shared_from_this<Node::Lambda> {
|
||||
public:
|
||||
Lambda(Node& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept :
|
||||
nf7::Node::Lambda(f, parent),
|
||||
file_(&f), log_(f.log_), handler_(f.FetchHandler()) {
|
||||
nf7::Node::Lambda(f, parent), file_(f.life_), log_(f.log_) {
|
||||
}
|
||||
|
||||
void Handle(std::string_view name, const nf7::Value& v,
|
||||
const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept override {
|
||||
const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept override
|
||||
try {
|
||||
file_.EnforceAlive();
|
||||
|
||||
auto fu = file_->FetchHandler();
|
||||
auto self = shared_from_this();
|
||||
handler_.ThenSub(self, [self, name = std::string(name), v, caller](auto) mutable {
|
||||
self->CallHandler({{std::move(name), std::move(v)}}, caller);
|
||||
fu.ThenSub(self, [self, name = std::string(name), v = v, caller](auto fu) mutable {
|
||||
self->CallHandler({std::move(name), std::move(v)}, fu, caller);
|
||||
});
|
||||
} catch (nf7::LifeExpiredException&) {
|
||||
} catch (nf7::Exception& e) {
|
||||
log_->Error(e.msg());
|
||||
}
|
||||
void Abort() noexcept override {
|
||||
for (auto& wth : th_) {
|
||||
@ -220,62 +185,53 @@ class Node::Lambda final : public nf7::Node::Lambda,
|
||||
}
|
||||
|
||||
private:
|
||||
Node* const file_;
|
||||
nf7::Life<Node>::Ref file_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> handler_;
|
||||
std::shared_ptr<nf7::luajit::Queue> ljq_;
|
||||
|
||||
std::vector<std::weak_ptr<nf7::luajit::Thread>> th_;
|
||||
|
||||
std::optional<nf7::luajit::Ref> ctxtable_;
|
||||
|
||||
|
||||
using Param = std::pair<std::string, nf7::Value>;
|
||||
void CallHandler(std::optional<Param>&& p, const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept
|
||||
void CallHandler(Param&& p, auto& fu, const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept
|
||||
try {
|
||||
auto self = shared_from_this();
|
||||
th_.erase(
|
||||
std::remove_if(th_.begin(), th_.end(), [](auto& x) { return x.expired(); }),
|
||||
th_.end());
|
||||
|
||||
auto handler = handler_.value();
|
||||
ljq_ = handler->ljq();
|
||||
auto handler = fu.value();
|
||||
auto ljq = handler->ljq();
|
||||
|
||||
env().GetFileOrThrow(initiator()); // check if the owner is alive
|
||||
auto th = std::make_shared<nf7::luajit::Thread>(
|
||||
self, ljq_,
|
||||
[self](auto& th, auto L) { self->HandleThread(th, L); });
|
||||
self, ljq, [self, ljq](auto& th, auto L) { self->HandleThread(ljq, th, L); });
|
||||
th->Install(log_);
|
||||
th_.emplace_back(th);
|
||||
|
||||
ljq_->Push(self, [this, self, p = std::move(p), caller, handler, th](auto L) mutable {
|
||||
ljq->Push(self, [this, self, ljq, p = std::move(p), caller, handler, th](auto L) mutable {
|
||||
auto thL = th->Init(L);
|
||||
lua_rawgeti(thL, LUA_REGISTRYINDEX, handler->index());
|
||||
if (p) {
|
||||
lua_pushstring(thL, p->first.c_str());
|
||||
nf7::luajit::PushValue(thL, p->second);
|
||||
} else {
|
||||
lua_pushnil(thL);
|
||||
lua_pushnil(thL);
|
||||
}
|
||||
lua_pushstring(thL, p.first.c_str());
|
||||
nf7::luajit::PushValue(thL, p.second);
|
||||
PushCaller(thL, caller);
|
||||
PushContextTable(thL);
|
||||
PushContextTable(ljq, thL);
|
||||
th->Resume(thL, 4);
|
||||
});
|
||||
} catch (nf7::Exception& e) {
|
||||
log_->Error("failed to call handler: "+e.msg());
|
||||
}
|
||||
|
||||
void HandleThread(nf7::luajit::Thread& th, lua_State* L) noexcept {
|
||||
void HandleThread(const std::shared_ptr<nf7::luajit::Queue>& ljq,
|
||||
nf7::luajit::Thread& th, lua_State* L) noexcept {
|
||||
switch (th.state()) {
|
||||
case nf7::luajit::Thread::kFinished:
|
||||
return;
|
||||
|
||||
case nf7::luajit::Thread::kPaused:
|
||||
log_->Warn("unexpected yield");
|
||||
ljq_->Push(shared_from_this(),
|
||||
ljq->Push(shared_from_this(),
|
||||
[th = th.shared_from_this(), L](auto) { th->Resume(L, 0); });
|
||||
return;
|
||||
|
||||
@ -320,12 +276,16 @@ class Node::Lambda final : public nf7::Node::Lambda,
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
void PushContextTable(lua_State* L) noexcept {
|
||||
void PushContextTable(const std::shared_ptr<nf7::luajit::Queue>& ljq,
|
||||
lua_State* L) noexcept {
|
||||
if (ctxtable_ && ctxtable_->ljq() != ljq) {
|
||||
ctxtable_ = std::nullopt;
|
||||
}
|
||||
if (!ctxtable_) {
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_pushvalue(L, -1);
|
||||
const int idx = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
ctxtable_.emplace(shared_from_this(), ljq_, idx);
|
||||
ctxtable_.emplace(shared_from_this(), ljq, idx);
|
||||
} else {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ctxtable_->index());
|
||||
}
|
||||
@ -337,21 +297,19 @@ std::shared_ptr<nf7::Node::Lambda> Node::CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
|
||||
return std::make_shared<Lambda>(*this, parent);
|
||||
}
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Node::FetchHandler() noexcept {
|
||||
if (handler_) return handler_;
|
||||
if (auto fetch = fetch_.lock()) return fetch->fu();
|
||||
|
||||
auto fetch = std::make_shared<FetchTask>(*this);
|
||||
fetch->Start();
|
||||
fetch_ = {fetch};
|
||||
return fetch->fu();
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Node::FetchHandler() noexcept
|
||||
try {
|
||||
return obj_.GetFileOrThrow().interfaceOrThrow<nf7::luajit::Obj>().Build();
|
||||
} catch (nf7::Exception&) {
|
||||
return {std::current_exception()};
|
||||
}
|
||||
|
||||
void Node::Handle(const Event& ev) noexcept {
|
||||
nf7::FileBase::Handle(ev);
|
||||
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
log_->SetUp(*this);
|
||||
FetchHandler();
|
||||
return;
|
||||
|
||||
case Event::kRemove:
|
||||
@ -363,6 +321,8 @@ void Node::Handle(const Event& ev) noexcept {
|
||||
}
|
||||
}
|
||||
void Node::Update() noexcept {
|
||||
nf7::FileBase::Update();
|
||||
|
||||
const auto& style = ImGui::GetStyle();
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
@ -371,22 +331,21 @@ void Node::Update() noexcept {
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("ConfigPopup")) {
|
||||
static std::string path;
|
||||
static std::string desc;
|
||||
static std::string in, out;
|
||||
static std::vector<std::string> invec, outvec;
|
||||
|
||||
ImGui::TextUnformatted("LuaJIT/Node: config");
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
path = obj_.path().Stringify();
|
||||
desc = desc;
|
||||
Join(in, data().inputs);
|
||||
Join(out, data().outputs);
|
||||
}
|
||||
|
||||
obj_editor_.ButtonWithLabel("obj factory");
|
||||
|
||||
const auto w = ImGui::CalcItemWidth()/2 - style.ItemSpacing.x/2;
|
||||
|
||||
ImGui::InputText("path", &path);
|
||||
ImGui::InputTextMultiline("description", &desc, {0, 4*em});
|
||||
ImGui::BeginGroup();
|
||||
ImGui::TextUnformatted("input:");
|
||||
@ -401,16 +360,6 @@ void Node::Update() noexcept {
|
||||
ImGui::TextUnformatted("sockets");
|
||||
|
||||
bool err = false;
|
||||
File::Path p;
|
||||
try {
|
||||
p = File::Path::Parse(path);
|
||||
ResolveOrThrow(p);
|
||||
} catch (File::NotFoundException&) {
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("path seems to be missing");
|
||||
} catch (nf7::Exception& e) {
|
||||
ImGui::Bullet(); ImGui::Text("invalid path: %s", e.msg().c_str());
|
||||
err = true;
|
||||
}
|
||||
try {
|
||||
Split(invec, in);
|
||||
} catch (nf7::Exception& e) {
|
||||
@ -428,8 +377,7 @@ void Node::Update() noexcept {
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(*this, "rebuilding node");
|
||||
env().ExecMain(ctx, [&, p = std::move(p)]() mutable {
|
||||
obj_ = std::move(p);
|
||||
env().ExecMain(ctx, [&]() mutable {
|
||||
data().desc = std::move(desc);
|
||||
data().inputs = std::move(invec);
|
||||
data().outputs = std::move(outvec);
|
||||
@ -443,17 +391,19 @@ void Node::UpdateMenu() noexcept {
|
||||
if (ImGui::MenuItem("config")) {
|
||||
popup_ = "ConfigPopup";
|
||||
}
|
||||
|
||||
obj_editor_.MenuWithTooltip("factory");
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("try fetch handler")) {
|
||||
FetchHandler();
|
||||
}
|
||||
if (ImGui::MenuItem("drop cached handler")) {
|
||||
DropHandler();
|
||||
}
|
||||
}
|
||||
void Node::UpdateTooltip() noexcept {
|
||||
ImGui::Text("path : %s", obj_.path().Stringify().c_str());
|
||||
ImGui::Text("handler: %s", handler_? "ready": "no");
|
||||
ImGui::Text("src:");
|
||||
ImGui::Indent();
|
||||
obj_editor_.Tooltip();
|
||||
ImGui::Unindent();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text("input:");
|
||||
@ -484,15 +434,6 @@ void Node::UpdateTooltip() noexcept {
|
||||
ImGui::TextUnformatted(data().desc.c_str());
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
ImGui::TextDisabled("drop a file here to set it as source");
|
||||
}
|
||||
void Node::UpdateDragDropTarget() noexcept {
|
||||
if (auto p = gui::dnd::Accept<Path>(gui::dnd::kFilePath)) {
|
||||
obj_ = std::move(*p);
|
||||
}
|
||||
}
|
||||
void Node::UpdateNode(Node::Editor&) noexcept {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,12 +13,15 @@
|
||||
|
||||
#include "common/async_buffer.hh"
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_ref.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/file_holder.hh"
|
||||
#include "common/future.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_watcher.hh"
|
||||
#include "common/gui_dnd.hh"
|
||||
#include "common/gui_file.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/lock.hh"
|
||||
#include "common/luajit.hh"
|
||||
#include "common/luajit_obj.hh"
|
||||
@ -36,10 +39,10 @@ using namespace std::literals;
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Obj final : public nf7::File, public nf7::DirItem, public nf7::luajit::Obj {
|
||||
class Obj final : public nf7::FileBase, public nf7::DirItem, public nf7::luajit::Obj {
|
||||
public:
|
||||
static inline const GenericTypeInfo<Obj> kType = {
|
||||
"LuaJIT/Obj", {"nf7::DirItem",}};
|
||||
static inline const nf7::GenericTypeInfo<Obj> kType = {
|
||||
"LuaJIT/Obj", {"nf7::DirItem", "nf7::luajit::Obj"}};
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted(
|
||||
"Compiles and runs LuaJIT script, and caches the object returned from the script.");
|
||||
@ -55,11 +58,24 @@ class Obj final : public nf7::File, public nf7::DirItem, public nf7::luajit::Obj
|
||||
|
||||
class ExecTask;
|
||||
|
||||
Obj(Env& env, Path&& path = {}) noexcept :
|
||||
File(kType, env),
|
||||
DirItem(DirItem::kTooltip | DirItem::kMenu | DirItem::kDragDropTarget),
|
||||
struct Data final {
|
||||
nf7::FileHolder::Tag src;
|
||||
};
|
||||
|
||||
Obj(Env& env, const nf7::FileHolder* src = nullptr, Data&& = {}) noexcept :
|
||||
nf7::FileBase(kType, env, {&src_, &src_editor_}),
|
||||
nf7::DirItem(nf7::DirItem::kTooltip |
|
||||
nf7::DirItem::kMenu),
|
||||
life_(*this),
|
||||
log_(std::make_shared<nf7::LoggerRef>()),
|
||||
src_(*this, std::move(path)) {
|
||||
src_(*this, "src", src),
|
||||
src_editor_(src_, [](auto& t) { return t.flags().contains("nf7::AsyncBuffer"); }),
|
||||
mem_({.src = {src_}}) {
|
||||
src_.onChildMementoChange = [this]() { mem_.Commit(); };
|
||||
src_.onEmplace = [this]() { mem_.Commit(); };
|
||||
|
||||
mem_.onRestore = [this]() { DropCache(); Touch(); };
|
||||
mem_.onCommit = [this]() { DropCache(); Touch(); };
|
||||
}
|
||||
|
||||
Obj(Env& env, Deserializer& ar) noexcept : Obj(env) {
|
||||
@ -69,22 +85,23 @@ class Obj final : public nf7::File, public nf7::DirItem, public nf7::luajit::Obj
|
||||
ar(src_);
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<Obj>(env, Path(src_.path()));
|
||||
return std::make_unique<Obj>(env, &src_, Data {mem_.data()});
|
||||
}
|
||||
|
||||
void Handle(const Event&) noexcept override;
|
||||
void Update() noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
void UpdateDragDropTarget() noexcept override;
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Build() noexcept override;
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<nf7::DirItem, nf7::luajit::Obj>(t).Select(this);
|
||||
return nf7::InterfaceSelector<
|
||||
nf7::DirItem, nf7::luajit::Obj, nf7::Memento>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Obj> life_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
std::optional<nf7::GenericWatcher> watcher_;
|
||||
@ -92,19 +109,19 @@ class Obj final : public nf7::File, public nf7::DirItem, public nf7::luajit::Obj
|
||||
|
||||
nf7::Task<std::shared_ptr<nf7::luajit::Ref>>::Holder exec_;
|
||||
|
||||
const char* popup_ = nullptr;
|
||||
nf7::FileHolder src_;
|
||||
nf7::gui::FileHolderEditor src_editor_;
|
||||
|
||||
// persistent params
|
||||
nf7::FileRef src_;
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
|
||||
|
||||
void Reset() noexcept;
|
||||
void DropCache() noexcept;
|
||||
};
|
||||
|
||||
class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>> {
|
||||
public:
|
||||
ExecTask(Obj& target) noexcept :
|
||||
Task(target.env(), target.id()), target_(&target), log_(target_->log_) {
|
||||
Task(target.env(), target.id()), target_(&target), log_(target.log_) {
|
||||
}
|
||||
|
||||
size_t GetMemoryUsage() const noexcept override {
|
||||
@ -112,7 +129,8 @@ class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>>
|
||||
}
|
||||
|
||||
private:
|
||||
Obj* target_;
|
||||
Obj* const target_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
std::string chunkname_;
|
||||
@ -123,13 +141,14 @@ class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>>
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>>::Coro Proc() noexcept override {
|
||||
try {
|
||||
auto& srcf = *target_->src_;
|
||||
auto& srcf = target_->src_.GetFileOrThrow();
|
||||
chunkname_ = srcf.abspath().Stringify();
|
||||
|
||||
const auto srcf_id = srcf.id();
|
||||
|
||||
// acquire lock of source
|
||||
auto src = srcf.interfaceOrThrow<nf7::AsyncBuffer>().self();
|
||||
auto srclock = co_await src->AcquireLock(false);
|
||||
log_->Trace("source file lock acquired");
|
||||
|
||||
// get size of source
|
||||
buf_size_ = co_await src->size();
|
||||
@ -147,31 +166,26 @@ class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>>
|
||||
throw nf7::Exception("failed to read all bytes from source");
|
||||
}
|
||||
|
||||
// create thread to compile lua script
|
||||
// find luajit queue
|
||||
auto ljq = target_->
|
||||
ResolveUpwardOrThrow("_luajit").
|
||||
interfaceOrThrow<nf7::luajit::Queue>().self();
|
||||
|
||||
// create promise handler for new luajit thread
|
||||
nf7::Future<int>::Promise lua_pro(self());
|
||||
auto handler = nf7::luajit::Thread::CreatePromiseHandler<int>(
|
||||
lua_pro, [&](auto L) {
|
||||
if (lua_gettop(L) != 1) {
|
||||
throw nf7::Exception("expected one object to be returned");
|
||||
}
|
||||
if (auto str = lua_tostring(L, -1)) {
|
||||
log_->Info("got '"s+str+"'");
|
||||
} else {
|
||||
log_->Info("got ["s+lua_typename(L, lua_type(L, -1))+"]");
|
||||
}
|
||||
return luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
});
|
||||
|
||||
// setup watcher
|
||||
// start watcher on target_->watcher_
|
||||
try {
|
||||
*target_->src_; // check if the src is alive
|
||||
|
||||
auto& w = target_->watcher_;
|
||||
w.emplace(env());
|
||||
w->Watch(srcf.id());
|
||||
w->Watch(srcf_id);
|
||||
|
||||
std::weak_ptr<Task> wself = self();
|
||||
w->AddHandler(Event::kUpdate, [t = target_, wself](auto&) {
|
||||
@ -203,10 +217,8 @@ class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>>
|
||||
|
||||
// wait for end of execution and return built object's index
|
||||
const int idx = co_await lua_pro.future();
|
||||
log_->Trace("task finished");
|
||||
|
||||
// context for object cache
|
||||
// TODO use specific Context type
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(env(), initiator(), "luajit object cache");
|
||||
|
||||
// return the object and cache it
|
||||
@ -214,7 +226,6 @@ class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>>
|
||||
co_yield target_->cache_;
|
||||
|
||||
} catch (Exception& e) {
|
||||
log_->Error(e.msg());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@ -239,7 +250,9 @@ class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>>
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Obj::Build() noexcept {
|
||||
if (auto exec = exec_.lock()) return exec->fu();
|
||||
if (cache_) return std::shared_ptr<nf7::luajit::Ref>{cache_};
|
||||
if (cache_) {
|
||||
return std::shared_ptr<nf7::luajit::Ref>{cache_};
|
||||
}
|
||||
|
||||
auto exec = std::make_shared<ExecTask>(*this);
|
||||
exec->Start();
|
||||
@ -247,14 +260,13 @@ nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Obj::Build() noexcept {
|
||||
return exec->fu();
|
||||
}
|
||||
void Obj::Handle(const Event& ev) noexcept {
|
||||
nf7::FileBase::Handle(ev);
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
log_->SetUp(*this);
|
||||
break;
|
||||
case Event::kRemove:
|
||||
exec_ = {};
|
||||
cache_ = nullptr;
|
||||
watcher_ = std::nullopt;
|
||||
DropCache();
|
||||
log_->TearDown();
|
||||
break;
|
||||
|
||||
@ -262,79 +274,25 @@ void Obj::Handle(const Event& ev) noexcept {
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Obj::Reset() noexcept {
|
||||
void Obj::DropCache() noexcept {
|
||||
exec_ = {};
|
||||
cache_ = nullptr;
|
||||
watcher_ = std::nullopt;
|
||||
}
|
||||
|
||||
void Obj::Update() noexcept {
|
||||
if (const auto popup = std::exchange(popup_, nullptr)) {
|
||||
ImGui::OpenPopup(popup);
|
||||
}
|
||||
if (ImGui::BeginPopup("ConfigPopup")) {
|
||||
static std::string path_str;
|
||||
ImGui::TextUnformatted("LuaJIT/Obj: config");
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
path_str = src_.path().Stringify();
|
||||
}
|
||||
|
||||
const bool submit = ImGui::InputText(
|
||||
"path", &path_str, ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
|
||||
Path path;
|
||||
bool err = false;
|
||||
try {
|
||||
path = Path::Parse(path_str);
|
||||
} catch (Exception& e) {
|
||||
ImGui::Bullet(); ImGui::Text("invalid path: %s", e.msg().c_str());
|
||||
err = true;
|
||||
}
|
||||
try {
|
||||
ResolveOrThrow(path);
|
||||
} catch (File::NotFoundException&) {
|
||||
ImGui::Bullet(); ImGui::Text("(target seems to be missing)");
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
if (ImGui::Button("ok") || submit) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
if (path != src_.path()) {
|
||||
auto task = [this, p = std::move(path)]() mutable {
|
||||
src_ = std::move(p);
|
||||
Reset();
|
||||
};
|
||||
auto ctx = std::make_shared<
|
||||
nf7::GenericContext>(*this, "changing source path");
|
||||
env().ExecMain(ctx, std::move(task));
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
void Obj::UpdateMenu() noexcept {
|
||||
if (ImGui::MenuItem("config")) {
|
||||
popup_ = "ConfigPopup";
|
||||
}
|
||||
src_editor_.MenuWithTooltip("src");
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("try build")) {
|
||||
Build();
|
||||
}
|
||||
if (ImGui::MenuItem("drop cache", nullptr, nullptr, !!cache_)) {
|
||||
Reset();
|
||||
DropCache();
|
||||
}
|
||||
}
|
||||
void Obj::UpdateTooltip() noexcept {
|
||||
ImGui::Text("source: %s", src_.path().Stringify().c_str());
|
||||
ImGui::Text("source: %s", src_editor_.GetDisplayText().c_str());
|
||||
ImGui::Text("cache : %d", cache_? cache_->index(): -1);
|
||||
ImGui::TextDisabled("drop a file here to set it as source");
|
||||
}
|
||||
void Obj::UpdateDragDropTarget() noexcept {
|
||||
if (auto p = gui::dnd::Accept<Path>(gui::dnd::kFilePath)) {
|
||||
src_ = std::move(*p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class Adaptor final : public nf7::FileBase, public nf7::Sequencer {
|
||||
};
|
||||
|
||||
Adaptor(Env& env, const nf7::FileHolder* target = nullptr, const Data* data = nullptr) noexcept :
|
||||
nf7::FileBase(kType, env, {&target_}),
|
||||
nf7::FileBase(kType, env, {&target_, &target_editor_}),
|
||||
Sequencer(Sequencer::kCustomItem |
|
||||
Sequencer::kTooltip |
|
||||
Sequencer::kParamPanel),
|
||||
|
@ -43,7 +43,7 @@ class Call final : public nf7::FileBase, public nf7::Sequencer {
|
||||
class SessionLambda;
|
||||
|
||||
Call(Env& env, const nf7::FileHolder* callee = nullptr, std::string_view expects = "") noexcept :
|
||||
FileBase(kType, env, {&callee_}),
|
||||
FileBase(kType, env, {&callee_, &callee_editor_}),
|
||||
Sequencer(Sequencer::kCustomItem |
|
||||
Sequencer::kTooltip |
|
||||
Sequencer::kParamPanel),
|
||||
|
Loading…
x
Reference in New Issue
Block a user