improve nf7::Node interface

This commit is contained in:
falsycat 2022-08-27 11:42:30 +09:00
parent 901f5c4ab9
commit cf771824dc
11 changed files with 180 additions and 77 deletions

View File

@ -47,6 +47,7 @@ class Window {
}
void SetFocus() noexcept {
shown_ = true;
set_focus_ = true;
}

View File

@ -22,12 +22,14 @@ class Node : public File::Interface {
class Lambda;
enum Flag : uint8_t {
kUI = 1 << 0, // UpdateNode() is called to display node
kMenu = 1 << 1,
kNone = 0,
kCustomNode = 1 << 0,
kMenu = 1 << 1,
kMenu_DirItem = 1 << 2, // use DirItem::UpdateMenu() method instead of Node's
};
using Flags = uint8_t;
Node(Flags f = 0) noexcept : flags_(f) { }
Node(Flags f) noexcept : flags_(f) { }
Node(const Node&) = default;
Node(Node&&) = default;
Node& operator=(const Node&) = default;

View File

@ -65,7 +65,8 @@ class Device final : public nf7::FileBase, public nf7::DirItem, public nf7::Node
}
Device(nf7::Env& env, Selector&& sel = size_t{0}, const ma_device_config& cfg = defaultConfig()) noexcept :
nf7::FileBase(kType, env),
nf7::DirItem(DirItem::kMenu | DirItem::kTooltip),
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kTooltip),
nf7::Node(nf7::Node::kNone),
data_(std::make_shared<AsyncData>(*this)),
selector_(std::move(sel)), cfg_(cfg),
config_popup_(std::make_shared<ConfigPopup>()) {
@ -90,7 +91,6 @@ class Device final : public nf7::FileBase, public nf7::DirItem, public nf7::Node
void Update() noexcept override;
void UpdateMenu() noexcept override;
void UpdateTooltip() noexcept override;
void UpdateNode(Node::Editor&) noexcept override { }
static bool UpdateModeSelector(ma_device_type*) noexcept;
static const ma_device_info* UpdateSelector(Selector*, ma_device_info*, size_t) noexcept;

View File

@ -59,6 +59,7 @@ class InlineNode final : public nf7::FileBase, public nf7::DirItem, public nf7::
InlineNode(nf7::Env& env, Data&& data = {}) noexcept :
nf7::FileBase(kType, env, {&socket_popup_}),
nf7::DirItem(nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kCustomNode),
life_(*this),
log_(std::make_shared<nf7::LoggerRef>(*this)),
mem_(std::move(data), *this) {

View File

@ -63,6 +63,7 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
Node(Env& env, Data&& data = {}) noexcept :
nf7::FileBase(kType, env, {&obj_, &obj_editor_, &socket_popup_}),
nf7::DirItem(nf7::DirItem::kTooltip | nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kNone),
life_(*this),
log_(std::make_shared<nf7::LoggerRef>(*this)),
obj_(*this, "obj_factory", mem_),
@ -294,7 +295,7 @@ void Node::UpdateWidget() noexcept {
mem_.Commit();
}
if (ImGui::Button("input/output list")) {
if (ImGui::Button("I/O list")) {
socket_popup_.Open(data().inputs, data().outputs);
}

View File

@ -46,7 +46,8 @@ class Imm final : public nf7::File, public nf7::DirItem, public nf7::Node {
Imm(nf7::Env& env, nf7::gui::Value&& v = {}) noexcept :
nf7::File(kType, env),
nf7::DirItem(DirItem::kWidget),
nf7::DirItem(nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kCustomNode),
life_(*this), mem_(std::move(v), *this) {
}

View File

@ -80,6 +80,7 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
nf7::DirItem(nf7::DirItem::kMenu |
nf7::DirItem::kTooltip |
nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kCustomNode),
life_(*this),
win_(*this, "Editor Node/Network", win),
items_(std::move(items)), links_(std::move(links)),
@ -118,7 +119,8 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
void UpdateMenu() noexcept override;
void UpdateTooltip() noexcept override;
void UpdateWidget() noexcept override;
void UpdateNode(Node::Editor&) noexcept override;
void UpdateMenu(nf7::Node::Editor&) noexcept override { UpdateMenu(); }
void UpdateNode(nf7::Node::Editor&) noexcept override;
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override;
@ -700,7 +702,8 @@ class Network::Initiator final : public nf7::File,
ImGui::Bullet(); ImGui::TextUnformatted("implements nf7::Node");
}
Initiator(nf7::Env& env) noexcept : File(kType, env) {
Initiator(nf7::Env& env) noexcept :
nf7::File(kType, env), nf7::Node(nf7::Node::kCustomNode) {
}
Initiator(nf7::Deserializer& ar) : Initiator(ar.env()) {
@ -751,7 +754,7 @@ class Network::Terminal : public nf7::File,
public nf7::Node,
public Network::InternalNode {
public:
static inline const GenericTypeInfo<Terminal> kType = {
static inline const nf7::GenericTypeInfo<Terminal> kType = {
"Node/Network/Terminal", {}};
enum Type { kInput, kOutput, };
@ -762,6 +765,7 @@ class Network::Terminal : public nf7::File,
Terminal(nf7::Env& env, Data&& data = {}) noexcept :
nf7::File(kType, env),
nf7::Node(nf7::Node::kCustomNode),
life_(*this), mem_(std::move(data), *this) {
}
@ -1128,19 +1132,20 @@ void Network::Update() noexcept {
}
}
void Network::UpdateMenu() noexcept {
if (ImGui::MenuItem("Editor", nullptr, &win_.shown()) && win_.shown()) {
win_.SetFocus();
}
if (ImGui::MenuItem("I/O sockets")) {
socket_popup_.Open(inputs_, outputs_);
}
ImGui::Separator();
ImGui::MenuItem("Network Editor", nullptr, &win_.shown());
}
void Network::UpdateTooltip() noexcept {
ImGui::Text("nodes active: %zu", items_.size());
}
void Network::UpdateWidget() noexcept {
ImGui::TextUnformatted("Node/Network");
if (ImGui::Button("open network editor")) {
win_.shown() = true;
if (ImGui::Button("open editor")) {
win_.SetFocus();
}
if (ImGui::Button("I/O sockets")) {
socket_popup_.Open(inputs_, outputs_);
@ -1148,7 +1153,23 @@ void Network::UpdateWidget() noexcept {
socket_popup_.Update();
}
void Network::UpdateNode(Node::Editor&) noexcept {
void Network::UpdateNode(nf7::Node::Editor&) noexcept {
ImGui::TextUnformatted("Node/Network");
ImGui::BeginGroup();
nf7::gui::NodeInputSockets(inputs_);
ImGui::SameLine();
nf7::gui::NodeOutputSockets(outputs_);
ImGui::EndGroup();
if (ImGui::Button("open editor")) {
win_.SetFocus();
}
if (ImGui::Button("I/O sockets")) {
socket_popup_.Open(inputs_, outputs_);
}
socket_popup_.Update();
}
void Network::Item::UpdateNode(Node::Editor& ed) noexcept {
@ -1157,7 +1178,14 @@ void Network::Item::UpdateNode(Node::Editor& ed) noexcept {
const auto id = reinterpret_cast<void*>(id_);
if (ImNodes::BeginNode(id, &pos_, &select_)) {
node_->UpdateNode(ed);
if (node_->flags() & nf7::Node::kCustomNode) {
node_->UpdateNode(ed);
} else {
ImGui::TextUnformatted(file_->type().name().c_str());
nf7::gui::NodeInputSockets(node_->GetInputs());
ImGui::SameLine();
nf7::gui::NodeOutputSockets(node_->GetOutputs());
}
}
ImNodes::EndNode();
@ -1179,6 +1207,16 @@ void Network::Item::UpdateNode(Node::Editor& ed) noexcept {
owner_->ExecAddItem(
std::make_unique<Item>(owner_->next_++, file_->Clone(env())));
}
if (node_->flags() & nf7::Node::kMenu_DirItem) {
ImGui::Separator();
auto dir = file_->interface<nf7::DirItem>();
assert(dir);
dir->UpdateMenu();
}
if (node_->flags() & nf7::Node::kMenu) {
ImGui::Separator();
node_->UpdateMenu(ed);
}
ImGui::EndPopup();
}

View File

@ -15,12 +15,14 @@
#include "nf7.hh"
#include "common/dir_item.hh"
#include "common/file_base.hh"
#include "common/generic_context.hh"
#include "common/generic_memento.hh"
#include "common/generic_type_info.hh"
#include "common/gui_dnd.hh"
#include "common/gui_node.hh"
#include "common/gui_popup.hh"
#include "common/life.hh"
#include "common/logger_ref.hh"
#include "common/memento.hh"
@ -55,10 +57,12 @@ class Ref final : public nf7::FileBase, public nf7::Node {
};
Ref(nf7::Env& env, Data&& data = {}) noexcept :
nf7::FileBase(kType, env),
nf7::FileBase(kType, env, {&config_popup_}),
nf7::Node(nf7::Node::kCustomNode | nf7::Node::kMenu),
life_(*this),
log_(std::make_shared<nf7::LoggerRef>(*this)),
mem_(std::move(data), *this) {
mem_(std::move(data), *this),
config_popup_(*this) {
nf7::FileBase::Install(*log_);
}
@ -81,8 +85,8 @@ class Ref final : public nf7::FileBase, public nf7::Node {
return data().outputs;
}
void Update() noexcept override;
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_);
@ -93,27 +97,43 @@ class Ref final : public nf7::FileBase, public nf7::Node {
std::shared_ptr<nf7::LoggerRef> log_;
const char* popup_ = nullptr;
nf7::GenericMemento<Data> mem_;
const Data& data() const noexcept { return mem_.data(); }
Data& data() noexcept { return mem_.data(); }
nf7::Node& target() const {
// GUI popup
class ConfigPopup final : public nf7::FileBase::Feature, private nf7::gui::Popup {
public:
ConfigPopup(Ref& f) noexcept : nf7::gui::Popup("ConfigPopup"), f_(&f) {
}
void Open() noexcept {
path_ = f_->data().target.Stringify();
nf7::gui::Popup::Open();
}
void Update() noexcept override;
private:
Ref* const f_;
std::string path_;
} config_popup_;
// accessors
nf7::File& target() const {
auto& f = ResolveOrThrow(data().target);
if (&f == this) throw nf7::Exception("self reference");
return f.interfaceOrThrow<nf7::Node>();
return f;
}
// socket synchronization
bool SyncQuiet() noexcept {
auto& dsti = data().inputs;
auto& dsto = data().outputs;
bool mod = false;
try {
auto& n = target();
auto& n = target().interfaceOrThrow<nf7::Node>();
const auto srci = n.GetInputs();
mod |= std::equal(dsti.begin(), dsti.end(), srci.begin(), srci.end());
@ -135,9 +155,17 @@ class Ref final : public nf7::FileBase, public nf7::Node {
mem_.Commit();
}
}
void ExecChangePath(Path&& p) noexcept {
void ExecSync() noexcept {
env().ExecMain(
std::make_shared<nf7::GenericContext>(*this, "synchornizing"),
[this]() { Sync(); });
}
// referencee operation
void ExecChangeTarget(Path&& p) noexcept {
auto& target = mem_.data().target;
if (p == target) return;
env().ExecMain(
std::make_shared<nf7::GenericContext>(*this, "change path"),
[this, &target, p = std::move(p)]() mutable {
@ -174,7 +202,9 @@ class Ref::Lambda final : public Node::Lambda,
log_->Error("stack overflow");
return;
}
base_ = f_->target().CreateLambda(shared_from_this());
base_ = f_->target().
interfaceOrThrow<nf7::Node>().
CreateLambda(shared_from_this());
}
base_->Handle(name, v, shared_from_this());
}
@ -206,46 +236,6 @@ try {
}
void Ref::Update() noexcept {
nf7::FileBase::Update();
if (auto popup = std::exchange(popup_, nullptr)) {
ImGui::OpenPopup(popup);
}
if (ImGui::BeginPopup("ConfigPopup")) {
static std::string pathstr;
if (ImGui::IsWindowAppearing()) {
pathstr = data().target.Stringify();
}
ImGui::TextUnformatted("Node/Ref: config");
const bool submit = ImGui::InputText(
"path", &pathstr, ImGuiInputTextFlags_EnterReturnsTrue);
bool err = false;
Path path;
try {
path = Path::Parse(pathstr);
} catch (nf7::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 && (ImGui::Button("ok") || submit)) {
ImGui::CloseCurrentPopup();
ExecChangePath(std::move(path));
}
ImGui::EndPopup();
}
}
void Ref::UpdateNode(Node::Editor&) noexcept {
const auto& style = ImGui::GetStyle();
const auto em = ImGui::GetFontSize();
@ -253,9 +243,7 @@ void Ref::UpdateNode(Node::Editor&) noexcept {
ImGui::TextUnformatted("Node/Ref");
ImGui::SameLine();
if (ImGui::SmallButton("sync")) {
env().ExecMain(
std::make_shared<nf7::GenericContext>(*this, "synchornizing with target node"),
[this]() { Sync(); });
ExecSync();
}
const auto pathstr = mem_.data().target.Stringify();
@ -277,11 +265,11 @@ void Ref::UpdateNode(Node::Editor&) noexcept {
}
if (ImGui::Button(pathstr.c_str(), {w, 0})) {
popup_ = "ConfigPopup";
config_popup_.Open();
}
if (ImGui::BeginDragDropTarget()) {
if (auto p = gui::dnd::Accept<Path>(gui::dnd::kFilePath)) {
ExecChangePath(std::move(*p));
ExecChangeTarget(std::move(*p));
}
ImGui::EndDragDropTarget();
}
@ -311,6 +299,69 @@ void Ref::UpdateNode(Node::Editor&) noexcept {
}
}
ImGui::EndGroup();
config_popup_.Update();
}
void Ref::UpdateMenu(nf7::Node::Editor& ed) noexcept {
if (ImGui::MenuItem("sync")) {
ExecSync();
}
if (ImGui::MenuItem("replace target")) {
config_popup_.Open();
}
try {
auto& f = target();
auto& n = f.interfaceOrThrow<nf7::Node>();
auto d = f.interface<nf7::DirItem>();
const bool dmenu = n.flags() & nf7::Node::kMenu_DirItem;
const bool menu = n.flags() & nf7::Node::kMenu;
if ((dmenu || menu) && ImGui::BeginMenu("target")) {
if (dmenu) {
assert(d);
ImGui::Separator();
d->UpdateMenu();
}
if (menu) {
ImGui::Separator();
n.UpdateMenu(ed);
}
ImGui::EndMenu();
}
} catch (nf7::Exception&) {
}
}
void Ref::ConfigPopup::Update() noexcept {
if (nf7::gui::Popup::Begin()) {
ImGui::TextUnformatted("Node/Ref: config");
const bool submit = ImGui::InputText(
"path", &path_, ImGuiInputTextFlags_EnterReturnsTrue);
bool err = false;
Path path;
try {
path = Path::Parse(path_);
} catch (nf7::Exception& e) {
ImGui::Bullet(); ImGui::Text("invalid path: %s", e.msg().c_str());
err = true;
}
try {
f_->ResolveOrThrow(path).interfaceOrThrow<nf7::Node>();
} catch (nf7::File::NotFoundException&) {
ImGui::Bullet(); ImGui::Text("target seems to be missing");
} catch (nf7::File::NotImplementedException&) {
ImGui::Bullet(); ImGui::Text("target doesn't seem to have Node interface");
}
if (!err && (ImGui::Button("ok") || submit)) {
ImGui::CloseCurrentPopup();
f_->ExecChangeTarget(std::move(path));
}
ImGui::EndPopup();
}
}
}

View File

@ -73,6 +73,7 @@ class TL final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
const nf7::gui::Window* win = nullptr) noexcept :
nf7::FileBase(kType, env, {&popup_socket_, &popup_add_item_}),
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kMenu_DirItem),
life_(*this),
layers_(std::move(layers)), next_(next),
win_(*this, "Timeline Editor", win), tl_("timeline"),
@ -1237,16 +1238,21 @@ void TL::Update() noexcept {
}
}
void TL::UpdateMenu() noexcept {
ImGui::MenuItem("Editor", nullptr, &win_.shown());
if (ImGui::MenuItem("editor", nullptr, &win_.shown()) && win_.shown()) {
win_.SetFocus();
}
if (ImGui::MenuItem("I/O list")) {
popup_socket_.Open(seq_inputs_, seq_outputs_);
}
}
void TL::UpdateWidget() noexcept {
ImGui::TextUnformatted("Sequencer/Timeline");
if (ImGui::Button("Timeline Editor")) {
win_.shown() = true;
if (ImGui::Button("Editor")) {
win_.SetFocus();
}
if (ImGui::Button("I/O socket list")) {
if (ImGui::Button("I/O list")) {
popup_socket_.Open(seq_inputs_, seq_outputs_);
}

View File

@ -84,6 +84,7 @@ class NativeFile final : public nf7::FileBase,
nf7::DirItem(nf7::DirItem::kMenu |
nf7::DirItem::kTooltip |
nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kMenu_DirItem),
life_(*this),
shared_(std::make_shared<SharedData>(*this)),
th_(std::make_shared<Thread>(*this, Runner {shared_})),

View File

@ -68,6 +68,7 @@ class Curve final : public nf7::File,
Curve(nf7::Env& env, Data&& data = {}) noexcept :
nf7::File(kType, env),
nf7::DirItem(nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kCustomNode),
nf7::Sequencer(nf7::Sequencer::kCustomItem |
nf7::Sequencer::kParamPanel),
life_(*this), mem_(std::move(data), *this) {