add UpdateConfig() to nf7::DirItem

This commit is contained in:
falsycat 2022-08-23 12:03:21 +09:00
parent f90dbc295b
commit 5f21f0c926
15 changed files with 440 additions and 442 deletions

View File

@ -66,6 +66,7 @@ target_sources(nf7
common/gui_file.cc
common/gui_node.hh
common/gui_popup.hh
common/gui_popup.cc
common/gui_timeline.hh
common/gui_timeline.cc
common/gui_value.hh
@ -96,6 +97,7 @@ target_sources(nf7
common/task.hh
common/thread.hh
common/timed_queue.hh
common/util_string.hh
common/value.hh
common/yas_audio.hh
common/yas_imgui.hh

View File

@ -14,7 +14,8 @@ class DirItem : public File::Interface {
kTree = 1 << 0,
kMenu = 1 << 1,
kTooltip = 1 << 2,
kDragDropTarget = 1 << 3,
kWidget = 1 << 3,
kDragDropTarget = 1 << 4,
};
using Flags = uint8_t;
@ -29,6 +30,7 @@ class DirItem : public File::Interface {
virtual void UpdateTree() noexcept { }
virtual void UpdateMenu() noexcept { }
virtual void UpdateTooltip() noexcept { }
virtual void UpdateWidget() noexcept { }
virtual void UpdateDragDropTarget() noexcept { }
Flags flags() const noexcept { return flags_; }

View File

@ -68,11 +68,14 @@ class FileHolder : public nf7::FileBase::Feature {
}
nf7::File& GetFileOrThrow() {
SetUp();
if (!file_) {
throw EmptyException {"holder is empty"};
if (auto f = GetFile()) {
return *f;
}
return *file_;
throw EmptyException {"holder is empty"};
}
nf7::File* GetFile() noexcept {
SetUp();
return file_;
}
// nf7::FileBase::Feature methods

View File

@ -95,7 +95,6 @@ bool FileFactory::Update() noexcept {
bool ret = false;
if (!err) {
if (ImGui::Button("ok")) {
ImGui::CloseCurrentPopup();
ret = true;
}
if (ImGui::IsItemHovered()) {
@ -115,11 +114,11 @@ bool FileFactory::Update() noexcept {
std::string FileHolderEditor::GetDisplayText() const noexcept {
std::string text;
if (holder_->own()) {
text = "OWN: " + holder_->file()->type().name();
text = "[OWN] " + holder_->GetFile()->type().name();
} else if (holder_->ref()) {
text = "REF: "s + holder_->path().Stringify();
text = "[REF] "s + holder_->path().Stringify();
} else if (holder_->empty()) {
text = "(NULL)";
text = "(empty)";
} else {
assert(false);
}
@ -131,12 +130,11 @@ void FileHolderEditor::Button(float w, bool small) noexcept {
ImGui::BeginGroup();
const auto text = GetDisplayText();
open_ |= small?
const bool open = small?
ImGui::SmallButton(text.c_str()):
ImGui::Button(text.c_str(), {w, 0});
if (ImGui::BeginPopupContextItem()) {
MenuItems();
ImGui::EndPopup();
if (open) {
ImGui::OpenPopup("FileHolderEmplacePopup_FromButton");
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
@ -144,6 +142,8 @@ void FileHolderEditor::Button(float w, bool small) noexcept {
ImGui::EndTooltip();
}
ImGui::EndGroup();
UpdateEmplacePopup("FileHolderEmplacePopup_FromButton");
ImGui::PopID();
}
void FileHolderEditor::ButtonWithLabel(const char* name) noexcept {
@ -163,34 +163,28 @@ void FileHolderEditor::Tooltip() noexcept {
}
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)) {
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
Tooltip();
ImGui::EndTooltip();
void FileHolderEditor::ItemWidget(const char* title) noexcept {
if (auto d = GetDirItem(*holder_, nf7::DirItem::kWidget)) {
if (ImGui::CollapsingHeader(title, ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::PushID(d);
ImGui::Indent();
d->UpdateWidget();
ImGui::Unindent();
ImGui::PopID();
}
MenuItems();
ImGui::EndMenu();
}
}
void FileHolderEditor::Update() noexcept {
ImGui::PushID(this);
if (std::exchange(open_, false)) {
ImGui::OpenPopup("FileHolderEditorPopup");
if (std::exchange(open_emplace_, false)) {
ImGui::OpenPopup("FileHolderEmplacePopup_FromMenu");
}
if (ImGui::BeginPopup("FileHolderEditorPopup")) {
UpdateEmplacePopup("FileHolderEmplacePopup_FromMenu");
ImGui::PopID();
}
void FileHolderEditor::UpdateEmplacePopup(const char* id) noexcept {
if (ImGui::BeginPopup(id)) {
if (ImGui::IsWindowAppearing()) {
if (holder_->ref()) {
type_ = kRef;
@ -208,6 +202,8 @@ void FileHolderEditor::Update() noexcept {
switch (type_) {
case kOwn:
if (factory_.Update()) {
ImGui::CloseCurrentPopup();
auto& f = holder_->owner();
f.env().ExecMain(
std::make_shared<nf7::GenericContext>(f),
@ -247,8 +243,6 @@ void FileHolderEditor::Update() noexcept {
}
ImGui::EndPopup();
}
ImGui::PopID();
}
} // namespace nf7::gui

View File

@ -69,19 +69,20 @@ class FileHolderEditor final : public nf7::FileBase::Feature {
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 ItemWidget(const char*) noexcept;
void Update() noexcept override;
private:
nf7::FileHolder* const holder_;
bool open_ = false;
bool open_emplace_ = false;
Type type_;
FileFactory factory_;
std::string path_;
void UpdateEmplacePopup(const char*) noexcept;
};
} // namespace nf7::gui

46
common/gui_popup.cc Normal file
View File

@ -0,0 +1,46 @@
#include "common/gui_popup.hh"
#include <imgui_stdlib.h>
#include "nf7.hh"
namespace nf7::gui {
void IOSocketListPopup::Update() noexcept {
if (Popup::Begin()) {
ImGui::InputTextMultiline("inputs", &is_);
ImGui::InputTextMultiline("outputs", &os_);
const auto iterm = nf7::util::SplitAndValidate(is_, nf7::File::Path::ValidateTerm);
const auto oterm = nf7::util::SplitAndValidate(os_, nf7::File::Path::ValidateTerm);
if (iterm) {
ImGui::Bullet();
ImGui::Text("invalid input name: %.*s", (int) iterm->size(), iterm->data());
}
if (oterm) {
ImGui::Bullet();
ImGui::Text("invalid output name: %.*s", (int) oterm->size(), oterm->data());
}
ImGui::Bullet();
ImGui::TextDisabled("duplicated names are removed automatically");
if (!iterm && !oterm && ImGui::Button("ok")) {
ImGui::CloseCurrentPopup();
std::vector<std::string> iv, ov;
nf7::util::SplitAndAppend(iv, is_);
nf7::util::Uniq(iv);
nf7::util::SplitAndAppend(ov, os_);
nf7::util::Uniq(ov);
onSubmit(std::move(iv), std::move(ov));
}
ImGui::EndPopup();
}
}
} // namespace nf7::gui

View File

@ -1,9 +1,14 @@
#include <functional>
#include <optional>
#include <string>
#include <vector>
#include <utility>
#include <imgui.h>
#include "common/file_base.hh"
#include "common/util_string.hh"
namespace nf7::gui {
@ -31,4 +36,29 @@ class Popup {
std::optional<ImGuiPopupFlags> open_flags_;
};
class IOSocketListPopup final :
public nf7::FileBase::Feature, private Popup {
public:
IOSocketListPopup(const char* name = "IOSocketListPopup",
ImGuiWindowFlags flags = 0) noexcept :
Popup(name, flags) {
}
void Open(std::span<const std::string> iv,
std::span<const std::string> ov) noexcept {
is_ = "";
nf7::util::JoinAndAppend(is_, iv);
os_ = "";
nf7::util::JoinAndAppend(os_, ov);
Popup::Open();
}
void Update() noexcept override;
std::function<void(std::vector<std::string>&&, std::vector<std::string>&&)> onSubmit =
[](auto&&, auto&&){};
private:
std::string is_, os_;
};
} // namespace nf7::gui

View File

@ -21,6 +21,7 @@
#include "common/generic_type_info.hh"
#include "common/generic_memento.hh"
#include "common/gui_file.hh"
#include "common/gui_popup.hh"
#include "common/life.hh"
#include "common/logger_ref.hh"
#include "common/luajit_obj.hh"
@ -31,6 +32,7 @@
#include "common/node.hh"
#include "common/ptr_selector.hh"
#include "common/task.hh"
#include "common/util_string.hh"
using namespace std::literals;
@ -59,8 +61,10 @@ 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_}),
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kTooltip),
nf7::FileBase(kType, env, {&obj_, &obj_editor_, &socket_popup_}),
nf7::DirItem(nf7::DirItem::kMenu |
nf7::DirItem::kTooltip |
nf7::DirItem::kWidget),
life_(*this),
log_(std::make_shared<nf7::LoggerRef>()),
obj_(*this, "obj_factory"),
@ -69,6 +73,16 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
mem_.data().obj.SetTarget(obj_);
mem_.CommitAmend();
socket_popup_.onSubmit = [this](auto&& i, auto&& o) {
this->env().ExecMain(
std::make_shared<nf7::GenericContext>(*this),
[this, i = std::move(i), o = std::move(o)]() {
mem_.data().inputs = std::move(i);
mem_.data().outputs = std::move(o);
mem_.Commit();
});
};
obj_.onChildMementoChange = [this]() { mem_.Commit(); };
obj_.onEmplace = [this]() { mem_.Commit(); };
@ -79,9 +93,8 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
Node(Env& env, Deserializer& ar) : Node(env) {
ar(obj_, data().desc, data().inputs, data().outputs);
// sanitize (remove duplicated sockets)
Uniq(data().inputs);
Uniq(data().outputs);
nf7::util::Uniq(data().inputs);
nf7::util::Uniq(data().outputs);
}
void Serialize(Serializer& ar) const noexcept override {
ar(obj_, data().desc, data().inputs, data().outputs);
@ -100,10 +113,9 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
}
void Handle(const Event&) noexcept override;
void Update() noexcept override;
static void UpdateList(std::vector<std::string>&) noexcept;
void UpdateMenu() noexcept override;
void UpdateTooltip() noexcept override;
void UpdateWidget() noexcept override;
File::Interface* interface(const std::type_info& t) noexcept override {
return nf7::InterfaceSelector<
@ -117,42 +129,17 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
nf7::Task<std::shared_ptr<nf7::luajit::Ref>>::Holder fetch_;
const char* popup_ = nullptr;
nf7::FileHolder obj_;
nf7::gui::FileHolderEditor obj_editor_;
nf7::gui::IOSocketListPopup socket_popup_;
nf7::GenericMemento<Data> mem_;
const Data& data() const noexcept { return mem_.data(); }
Data& data() noexcept { return mem_.data(); }
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> FetchHandler() noexcept;
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)) {
itr = v.erase(itr);
} else {
++itr;
}
}
}
static void Join(std::string& str, const std::vector<std::string>& vec) noexcept {
str.clear();
for (const auto& name : vec) str += name + "\n";
}
static void Split(std::vector<std::string>& vec, const std::string& str) {
vec.clear();
for (size_t i = 0; i < str.size(); ++i) {
auto j = str.find('\n', i);
if (j == std::string::npos) j = str.size();
auto name = str.substr(i, j-i);
File::Path::ValidateTerm(name);
vec.push_back(std::move(name));
i = j;
}
}
};
class Node::Lambda final : public nf7::Node::Lambda,
@ -320,82 +307,8 @@ void Node::Handle(const Event& ev) noexcept {
return;
}
}
void Node::Update() noexcept {
nf7::FileBase::Update();
const auto& style = ImGui::GetStyle();
const auto em = ImGui::GetFontSize();
if (const char* popup = std::exchange(popup_, nullptr)) {
ImGui::OpenPopup(popup);
}
if (ImGui::BeginPopup("ConfigPopup")) {
static std::string desc;
static std::string in, out;
static std::vector<std::string> invec, outvec;
ImGui::TextUnformatted("LuaJIT/Node: config");
if (ImGui::IsWindowAppearing()) {
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::InputTextMultiline("description", &desc, {0, 4*em});
ImGui::BeginGroup();
ImGui::TextUnformatted("input:");
ImGui::InputTextMultiline("##input", &in, {w, 0});
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::TextUnformatted("output:");
ImGui::InputTextMultiline("##output", &out, {w, 0});
ImGui::EndGroup();
ImGui::SameLine(0, style.ItemInnerSpacing.x);
ImGui::TextUnformatted("sockets");
bool err = false;
try {
Split(invec, in);
} catch (nf7::Exception& e) {
ImGui::Bullet(); ImGui::Text("invalid inputs: %s", e.msg().c_str());
err = true;
}
try {
Split(outvec, out);
} catch (nf7::Exception& e) {
ImGui::Bullet(); ImGui::Text("invalid outputs: %s", e.msg().c_str());
err = true;
}
if (!err && ImGui::Button("ok")) {
ImGui::CloseCurrentPopup();
auto ctx = std::make_shared<nf7::GenericContext>(*this, "rebuilding node");
env().ExecMain(ctx, [&]() mutable {
data().desc = std::move(desc);
data().inputs = std::move(invec);
data().outputs = std::move(outvec);
mem_.Commit();
});
}
ImGui::EndPopup();
}
}
void Node::UpdateMenu() noexcept {
if (ImGui::MenuItem("config")) {
popup_ = "ConfigPopup";
}
obj_editor_.MenuWithTooltip("factory");
ImGui::Separator();
if (ImGui::MenuItem("try fetch handler")) {
if (ImGui::MenuItem("fetch handler")) {
FetchHandler();
}
}
@ -435,6 +348,26 @@ void Node::UpdateTooltip() noexcept {
}
ImGui::Unindent();
}
void Node::UpdateWidget() noexcept {
const auto em = ImGui::GetFontSize();
ImGui::TextUnformatted("LuaJIT/Node: config");
obj_editor_.ButtonWithLabel("obj factory");
ImGui::InputTextMultiline("description", &data().desc, {0, 4*em});
if (ImGui::IsItemDeactivatedAfterEdit()) {
mem_.Commit();
}
if (ImGui::Button("input/output list")) {
socket_popup_.Open(data().inputs, data().outputs);
}
ImGui::Spacing();
obj_editor_.ItemWidget("obj factory");
socket_popup_.Update();
}
}
} // namespace nf7

View File

@ -65,7 +65,8 @@ class Obj final : public nf7::FileBase, public nf7::DirItem, public nf7::luajit:
Obj(Env& env, Data&& data = {}) noexcept :
nf7::FileBase(kType, env, {&src_, &src_editor_}),
nf7::DirItem(nf7::DirItem::kTooltip |
nf7::DirItem::kMenu),
nf7::DirItem::kMenu |
nf7::DirItem::kWidget),
life_(*this),
log_(std::make_shared<nf7::LoggerRef>()),
src_(*this, "src"),
@ -94,6 +95,7 @@ class Obj final : public nf7::FileBase, public nf7::DirItem, public nf7::luajit:
void Handle(const Event&) noexcept override;
void UpdateMenu() noexcept override;
void UpdateTooltip() noexcept override;
void UpdateWidget() noexcept override;
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Build() noexcept override;
@ -284,9 +286,7 @@ void Obj::DropCache() noexcept {
}
void Obj::UpdateMenu() noexcept {
src_editor_.MenuWithTooltip("src");
ImGui::Separator();
if (ImGui::MenuItem("try build")) {
if (ImGui::MenuItem("build")) {
Build();
}
if (ImGui::MenuItem("drop cache", nullptr, nullptr, !!cache_)) {
@ -300,6 +300,12 @@ void Obj::UpdateTooltip() noexcept {
src_editor_.Tooltip();
ImGui::Unindent();
}
void Obj::UpdateWidget() noexcept {
ImGui::TextUnformatted("LuaJIT/Obj: config");
src_editor_.ButtonWithLabel("src");
ImGui::Spacing();
src_editor_.ItemWidget("src");
}
}
} // namespace nf7

View File

@ -77,13 +77,17 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
ItemList&& items = {},
NodeLinkStore&& links = {}) :
nf7::FileBase(kType, env, {&add_popup_, &socket_popup_}),
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kTooltip),
nf7::DirItem(nf7::DirItem::kMenu |
nf7::DirItem::kTooltip |
nf7::DirItem::kWidget),
life_(*this),
win_(*this, "Editor Node/Network", win),
items_(std::move(items)), links_(std::move(links)),
add_popup_(*this), socket_popup_(*this) {
add_popup_(*this) {
socket_popup_.onSubmit = [this](auto&& i, auto&& o) {
ExecSwapSocket(std::move(i), std::move(o));
};
Initialize();
win_.shown() = true;
}
~Network() noexcept {
history_.Clear();
@ -108,11 +112,13 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
File* Find(std::string_view name) const noexcept;
void Handle(const Event& ev) noexcept override;
void Update() noexcept override;
void UpdateMenu() noexcept override;
void UpdateTooltip() noexcept override;
void UpdateWidget() noexcept override;
void UpdateNode(Node::Editor&) noexcept override;
void Handle(const Event& ev) noexcept override;
std::shared_ptr<Node::Lambda> CreateLambda(
const std::shared_ptr<Node::Lambda>&) noexcept override;
@ -150,6 +156,8 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
std::vector<std::string> inputs_, outputs_;
// GUI popup
nf7::gui::IOSocketListPopup socket_popup_;
class AddPopup final : public nf7::FileBase::Feature, private nf7::gui::Popup {
public:
static bool TypeFilter(const nf7::File::TypeInfo& t) noexcept {
@ -170,24 +178,6 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
nf7::gui::FileFactory factory_;
ImVec2 pos_;
} add_popup_;
class SocketPopup final : public nf7::FileBase::Feature, private nf7::gui::Popup {
public:
SocketPopup(Network& owner) noexcept :
nf7::gui::Popup("SocketPopup"), owner_(&owner) {
}
void Open() noexcept;
void Update() noexcept override;
static std::string Join(std::span<const std::string>) noexcept;
static std::vector<std::string> Parse(std::string_view);
private:
Network* const owner_;
std::string inputs_;
std::string outputs_;
} socket_popup_;
void Initialize();
@ -205,6 +195,8 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
[this]() { history_.ReDo(); Touch(); });
}
void ExecSwapSocket(std::vector<std::string>&&, std::vector<std::string>&&) noexcept;
Item& GetItem(ItemId id) const {
auto itr = item_map_.find(id);
if (itr == item_map_.end()) {
@ -575,6 +567,12 @@ class Network::SocketSwapCommand final : public nf7::History::Command {
std::swap(owner_->outputs_, pair_.out);
}
};
void Network::ExecSwapSocket(std::vector<std::string>&& i, std::vector<std::string>&& o) noexcept {
auto cmd = std::make_unique<SocketSwapCommand>(
*this, SocketSwapCommand::Pair {std::move(i), std::move(o)});
auto ctx = std::make_shared<nf7::GenericContext>(*this);
history_.Add(std::move(cmd)).ExecApply(ctx);
}
// A command that add or remove a Node.
class Network::Item::SwapCommand final : public nf7::History::Command {
@ -1030,9 +1028,6 @@ void Network::Update() noexcept {
const auto pos = mouse - canvas_pos - canvas_.Offset/canvas_.Zoom;
add_popup_.Open(pos);
}
if (ImGui::MenuItem("I/O sockets")) {
socket_popup_.Open();
}
ImGui::Separator();
if (ImGui::MenuItem("undo", nullptr, false, !!history_.prev())) {
UnDo();
@ -1044,6 +1039,10 @@ void Network::Update() noexcept {
if (ImGui::MenuItem("reset canvas zoom")) {
canvas_.Zoom = 1.f;
}
ImGui::Separator();
if (ImGui::MenuItem("I/O socket list")) {
socket_popup_.Open(inputs_, outputs_);
}
ImGui::EndPopup();
}
}
@ -1057,11 +1056,26 @@ void Network::Update() noexcept {
}
}
void Network::UpdateMenu() noexcept {
ImGui::MenuItem("shown", nullptr, &win_.shown());
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("I/O sockets")) {
socket_popup_.Open(inputs_, outputs_);
}
socket_popup_.Update();
}
void Network::UpdateNode(Node::Editor&) noexcept {
}
@ -1114,6 +1128,8 @@ void Network::AddPopup::Update() noexcept {
if (nf7::gui::Popup::Begin()) {
ImGui::TextUnformatted("Node/Network: adding new Node...");
if (factory_.Update()) {
ImGui::CloseCurrentPopup();
auto item = std::make_unique<Item>(owner_->next_++, factory_.Create(owner_->env()));
auto ctx = std::make_shared<nf7::GenericContext>(*owner_, "adding new node");
@ -1130,65 +1146,6 @@ void Network::AddPopup::Update() noexcept {
}
void Network::SocketPopup::Open() noexcept {
nf7::gui::Popup::Open();
inputs_ = Join(owner_->inputs_);
outputs_ = Join(owner_->outputs_);
}
void Network::SocketPopup::Update() noexcept {
if (nf7::gui::Popup::Begin()) {
ImGui::InputTextMultiline("input", &inputs_);
ImGui::InputTextMultiline("output", &outputs_);
try {
auto p = Network::SocketSwapCommand::Pair {
.in = Parse(inputs_),
.out = Parse(outputs_),
};
if (ImGui::Button("ok")) {
ImGui::CloseCurrentPopup();
auto cmd = std::make_unique<Network::SocketSwapCommand>(*owner_, std::move(p));
auto ctx = std::make_shared<nf7::GenericContext>(*owner_, "updating IO sockets");
owner_->history_.Add(std::move(cmd)).ExecApply(ctx);
}
} catch (nf7::Exception& e) {
ImGui::Bullet(); ImGui::TextUnformatted(e.msg().c_str());
}
ImGui::EndPopup();
}
}
std::string Network::SocketPopup::Join(std::span<const std::string> items) noexcept {
std::stringstream st;
for (const auto& item : items) {
st << item << '\n';
}
return st.str();
}
std::vector<std::string> Network::SocketPopup::Parse(std::string_view str) {
if (str.size() > 10*1024) {
throw nf7::Exception {"too long text"};
}
std::vector<std::string> ret;
std::string_view::size_type pos = 0;
while (pos < str.size()) {
auto next = str.find('\n', pos);
if (next == std::string_view::npos) {
next = str.size();
}
const auto name = str.substr(pos, next-pos);
pos = next+1;
nf7::File::Path::ValidateTerm(name);
if (ret.end() != std::find(ret.begin(), ret.end(), name)) {
throw nf7::Exception {"name duplication ("+std::string{name}+")"};
}
ret.push_back(std::string {name});
}
return ret;
}
void Network::Initiator::UpdateNode(nf7::Node::Editor& ed) noexcept {
ImGui::TextUnformatted("INITIATOR");

View File

@ -143,13 +143,10 @@ class Adaptor::Session final : public nf7::Sequencer::Session {
}
const nf7::Value* Peek(std::string_view name) noexcept override {
assert(parent_);
auto itr = vars_.find(std::string {name});
return itr != vars_.end()? &itr->second: nullptr;
}
std::optional<nf7::Value> Receive(std::string_view name) noexcept override {
assert(parent_);
auto itr = vars_.find(std::string {name});
if (itr == vars_.end()) {
return std::nullopt;
@ -160,7 +157,8 @@ class Adaptor::Session final : public nf7::Sequencer::Session {
}
void Send(std::string_view name, nf7::Value&& v) noexcept override {
assert(parent_);
if (done_) return;
auto itr = outs_.find(std::string {name});
if (itr != outs_.end()) {
parent_->Send(itr->second, std::move(v));
@ -170,7 +168,7 @@ class Adaptor::Session final : public nf7::Sequencer::Session {
void Finish() noexcept override {
assert(parent_);
parent_->Finish();
parent_ = nullptr;
done_ = true;
}
private:
@ -178,6 +176,8 @@ class Adaptor::Session final : public nf7::Sequencer::Session {
std::unordered_map<std::string, nf7::Value> vars_;
std::unordered_map<std::string, std::string> outs_;
bool done_ = false;
};
class Adaptor::Lambda final : public nf7::Sequencer::Lambda,
public std::enable_shared_from_this<Adaptor::Lambda> {

View File

@ -249,6 +249,8 @@ void Call::UpdateParamPanel(Sequencer::Editor&) noexcept {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("callee's lambda is created for each session");
}
ImGui::Spacing();
callee_editor_.ItemWidget("callee");
}
}
void Call::UpdateTooltip(Sequencer::Editor&) noexcept {

View File

@ -22,6 +22,7 @@
#include "nf7.hh"
#include "common/dir_item.hh"
#include "common/file_base.hh"
#include "common/generic_context.hh"
#include "common/generic_type_info.hh"
#include "common/gui_context.hh"
@ -44,7 +45,7 @@ using namespace std::literals;
namespace nf7 {
namespace {
class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
class TL final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
public:
static inline const nf7::GenericTypeInfo<TL> kType = {
"Sequencer/Timeline", {"nf7::DirItem"}};
@ -71,12 +72,17 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
std::vector<std::unique_ptr<Layer>>&& layers = {},
ItemId next = 1,
const nf7::gui::Window* win = nullptr) noexcept :
nf7::File(kType, env), nf7::DirItem(nf7::DirItem::kMenu),
nf7::FileBase(kType, env, {&popup_socket_, &popup_add_item_}),
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kWidget),
life_(*this),
length_(length), layers_(std::move(layers)), next_(next),
win_(*this, "Timeline Editor", win), tl_("timeline"),
popup_add_item_(*this), popup_config_(*this) {
popup_add_item_(*this) {
ApplySeqSocketChanges();
popup_socket_.onSubmit = [this](auto&& i, auto&& o) {
ExecChangeSeqSocket(std::move(i), std::move(o));
};
}
~TL() noexcept {
history_.Clear();
@ -104,6 +110,7 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
void Handle(const Event& ev) noexcept;
void Update() noexcept override;
void UpdateMenu() noexcept override;
void UpdateWidget() noexcept override;
void UpdateEditorWindow() noexcept;
void UpdateLambdaSelector() noexcept;
@ -139,8 +146,11 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
nf7::gui::Timeline tl_;
// popup
struct AddItemPopup final : nf7::gui::Popup {
// GUI popup
nf7::gui::IOSocketListPopup popup_socket_;
struct AddItemPopup final :
public nf7::FileBase::Feature, private nf7::gui::Popup {
public:
AddItemPopup(TL& f) noexcept :
Popup("AddItemPopup"),
@ -153,7 +163,7 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
target_layer_ = &l;
Popup::Open();
}
void Update() noexcept;
void Update() noexcept override;
private:
TL* const owner_;
@ -163,27 +173,6 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
nf7::gui::FileFactory factory_;
} popup_add_item_;
struct ConfigPopup final : nf7::gui::Popup {
public:
ConfigPopup(TL& f) noexcept :
Popup("ConfigPopup"), owner_(&f) {
}
void Open() noexcept {
inputs_ = StringifySocketList(owner_->seq_inputs_);
outputs_ = StringifySocketList(owner_->seq_outputs_);
error_ = "";
Popup::Open();
}
void Update() noexcept;
private:
TL* const owner_;
std::string inputs_;
std::string outputs_;
std::string error_;
} popup_config_;
// GUI temporary params
@ -226,6 +215,7 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
void AttachLambda(const std::shared_ptr<TL::Lambda>&) noexcept;
// socket operation
void ExecChangeSeqSocket(std::vector<std::string>&&, std::vector<std::string>&&) noexcept;
void ApplySeqSocketChanges() noexcept {
inputs_ = seq_inputs_;
inputs_.push_back("_exec");
@ -233,42 +223,6 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
outputs_ = seq_outputs_;
outputs_.push_back("_cursor");
}
static std::vector<std::string> ParseSocketList(std::string_view names) {
const auto n = names.size();
std::vector<std::string> ret;
size_t begin = 0;
for (size_t i = 0; i <= n; ++i) {
if (i == n || names[i] == '\n') {
const auto name = names.substr(begin, i-begin);
if (name.size() > 0) {
ret.push_back(std::string {name});
}
begin = i+1;
continue;
}
}
ValidateSocketList(ret);
return ret;
}
static void ValidateSocketList(const std::vector<std::string>& a) {
for (size_t i = 0; i < a.size(); ++i) {
File::Path::ValidateTerm(a[i]);
for (size_t j = i+1; j < a.size(); ++j) {
if (a[i] == a[j]) {
throw nf7::Exception {"name duplication: "+a[i]};
}
}
}
}
static std::string StringifySocketList(const std::vector<std::string>& a) noexcept {
std::stringstream st;
for (auto& name : a) {
st << name << '\n';
}
return st.str();
}
};
@ -1228,6 +1182,14 @@ class TL::ConfigModifyCommand final : public nf7::History::Command {
}
}
};
void TL::ExecChangeSeqSocket(std::vector<std::string>&& i, std::vector<std::string>&& o) noexcept {
auto cmd = ConfigModifyCommand::Builder {*this}.
inputs(std::move(i)).
outputs(std::move(o)).
Build();
auto ctx = std::make_shared<nf7::GenericContext>(*this, "updating I/O socket list");
history_.Add(std::move(cmd)).ExecApply(ctx);
}
std::unique_ptr<nf7::File> TL::Clone(nf7::Env& env) const noexcept {
@ -1238,6 +1200,8 @@ std::unique_ptr<nf7::File> TL::Clone(nf7::Env& env) const noexcept {
return std::make_unique<TL>(env, length_, std::move(layers), next, &win_);
}
void TL::Handle(const Event& ev) noexcept {
nf7::FileBase::Handle(ev);
switch (ev.type) {
case Event::kAdd:
if (layers_.size() == 0) {
@ -1268,15 +1232,14 @@ void TL::Handle(const Event& ev) noexcept {
}
void TL::Update() noexcept {
nf7::FileBase::Update();
for (const auto& layer : layers_) {
for (const auto& item : layer->items()) {
item->file().Update();
}
}
popup_add_item_.Update();
popup_config_.Update();
UpdateEditorWindow();
UpdateParamPanelWindow();
@ -1288,6 +1251,15 @@ void TL::Update() noexcept {
void TL::UpdateMenu() noexcept {
ImGui::MenuItem("Editor", nullptr, &win_.shown());
}
void TL::UpdateWidget() noexcept {
ImGui::TextUnformatted("Sequencer/Timeline");
if (ImGui::Button("I/O socket list")) {
popup_socket_.Open(seq_inputs_, seq_outputs_);
}
popup_socket_.Update();
}
void TL::UpdateEditorWindow() noexcept {
const auto kInit = []() {
const auto em = ImGui::GetFontSize();
@ -1324,8 +1296,8 @@ void TL::UpdateEditorWindow() noexcept {
ExecReDo();
}
ImGui::Separator();
if (ImGui::MenuItem("config")) {
popup_config_.Open();
if (ImGui::MenuItem("I/O socket list")) {
popup_socket_.Open(seq_inputs_, seq_outputs_);
}
ImGui::EndPopup();
}
@ -1638,33 +1610,6 @@ void TL::AddItemPopup::Update() noexcept {
ImGui::EndPopup();
}
}
void TL::ConfigPopup::Update() noexcept {
if (Popup::Begin()) {
ImGui::TextUnformatted("Sequencer/Timeline: updating config...");
ImGui::InputTextMultiline("inputs", &inputs_);
ImGui::InputTextMultiline("outputs", &outputs_);
if (ImGui::Button("ok")) {
try {
auto cmd = ConfigModifyCommand::Builder(*owner_).
inputs(ParseSocketList(inputs_)).
outputs(ParseSocketList(outputs_)).
Build();
ImGui::CloseCurrentPopup();
auto ctx = std::make_shared<nf7::GenericContext>(*owner_, "updating config");
owner_->history_.Add(std::move(cmd)).ExecApply(ctx);
} catch (nf7::Exception& e) {
error_ = e.msg();
}
}
if (error_.size() > 0) {
ImGui::Bullet();
ImGui::TextUnformatted(error_.c_str());
}
ImGui::EndPopup();
}
}
}
} // namespace nf7

View File

@ -16,10 +16,12 @@
#include "common/dir.hh"
#include "common/dir_item.hh"
#include "common/file_base.hh"
#include "common/generic_context.hh"
#include "common/generic_type_info.hh"
#include "common/gui_dnd.hh"
#include "common/gui_file.hh"
#include "common/gui_popup.hh"
#include "common/gui_window.hh"
#include "common/ptr_selector.hh"
#include "common/yas_nf7.hh"
@ -28,7 +30,7 @@
namespace nf7 {
namespace {
class Dir final : public File,
class Dir final : public nf7::FileBase,
public nf7::Dir,
public nf7::DirItem {
public:
@ -38,15 +40,13 @@ class Dir final : public File,
using ItemMap = std::map<std::string, std::unique_ptr<File>>;
Dir(Env& env, ItemMap&& items = {}, const gui::Window* src = nullptr) noexcept :
File(kType, env),
DirItem(nf7::DirItem::kTree |
nf7::DirItem::kMenu |
nf7::DirItem::kTooltip |
nf7::DirItem::kDragDropTarget),
factory_(*this, [](auto& t) { return t.flags().contains("nf7::DirItem"); },
nf7::gui::FileFactory::kNameInput |
nf7::gui::FileFactory::kNameDupCheck),
items_(std::move(items)), win_(*this, "TreeView System/Dir", src) {
nf7::FileBase(kType, env, {&widget_popup_, &add_popup_, &rename_popup_}),
nf7::DirItem(nf7::DirItem::kTree |
nf7::DirItem::kMenu |
nf7::DirItem::kTooltip |
nf7::DirItem::kDragDropTarget),
items_(std::move(items)), win_(*this, "TreeView System/Dir", src),
widget_popup_(*this), add_popup_(*this), rename_popup_(*this) {
}
Dir(Env& env, Deserializer& ar) : Dir(env) {
@ -96,6 +96,7 @@ class Dir final : public File,
void UpdateDragDropTarget() noexcept override;
void Handle(const Event& ev) noexcept override {
nf7::FileBase::Handle(ev);
switch (ev.type) {
case Event::kAdd:
for (const auto& item : items_) item.second->MoveUnder(*this, item.first);
@ -117,12 +118,6 @@ class Dir final : public File,
}
private:
const char* popup_ = nullptr;
std::string rename_target_;
nf7::gui::FileFactory factory_;
// persistent params
ItemMap items_;
gui::Window win_;
@ -130,6 +125,66 @@ class Dir final : public File,
std::unordered_set<std::string> opened_;
// GUI popup
class WidgetPopup final :
public nf7::FileBase::Feature, private nf7::gui::Popup {
public:
WidgetPopup(Dir& owner) noexcept :
nf7::gui::Popup("WidgetPopup"), owner_(&owner) {
}
void Open(nf7::File& f) noexcept {
target_ = &f;
nf7::gui::Popup::Open();
}
void Update() noexcept override;
private:
Dir* owner_;
nf7::File* target_ = nullptr;
} widget_popup_;
class AddPopup final :
public nf7::FileBase::Feature, private nf7::gui::Popup {
public:
AddPopup(Dir& owner) noexcept :
nf7::gui::Popup("AddPopup"),
owner_(&owner),
factory_(owner, [](auto& t) { return t.flags().contains("nf7::DirItem"); },
nf7::gui::FileFactory::kNameInput |
nf7::gui::FileFactory::kNameDupCheck) {
}
using nf7::gui::Popup::Open;
void Update() noexcept override;
private:
Dir* owner_;
nf7::gui::FileFactory factory_;
} add_popup_;
class RenamePopup final :
public nf7::FileBase::Feature, private nf7::gui::Popup {
public:
RenamePopup(Dir& owner) noexcept :
nf7::gui::Popup("RenamePopup"),
owner_(&owner) {
}
void Open(std::string_view before) noexcept {
before_ = before;
after_ = "";
nf7::gui::Popup::Open();
}
void Update() noexcept override;
private:
Dir* owner_;
std::string before_;
std::string after_;
} rename_popup_;
std::string GetUniqueName(std::string_view name) const noexcept {
auto ret = std::string {name};
while (Find(ret)) {
@ -140,6 +195,8 @@ class Dir final : public File,
};
void Dir::Update() noexcept {
nf7::FileBase::Update();
const auto em = ImGui::GetFontSize();
// update children
@ -149,74 +206,6 @@ void Dir::Update() noexcept {
ImGui::PopID();
}
if (const auto popup = std::exchange(popup_, nullptr)) {
ImGui::OpenPopup(popup);
}
// new item popup
if (ImGui::BeginPopup("NewItemPopup")) {
ImGui::TextUnformatted("System/Dir: adding new file...");
if (factory_.Update()) {
auto ctx = std::make_shared<nf7::GenericContext>(*this, "adding new item");
auto task = [this]() { Add(factory_.name(), factory_.Create(env())); };
env().ExecMain(ctx, std::move(task));
}
ImGui::EndPopup();
}
// rename popup
if (ImGui::BeginPopup("RenamePopup")) {
static std::string new_name;
ImGui::TextUnformatted("System/Dir: renaming an exsting item...");
ImGui::InputText("before", &rename_target_);
bool submit = false;
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
if (ImGui::InputText("after", &new_name, ImGuiInputTextFlags_EnterReturnsTrue)) {
submit = true;
}
bool err = false;
if (!Find(rename_target_)) {
ImGui::Bullet(); ImGui::TextUnformatted("before is invalid: missing target");
err = true;
}
if (Find(new_name)) {
ImGui::Bullet(); ImGui::TextUnformatted("after is invalid: duplicated name");
err = true;
}
try {
Path::ValidateTerm(new_name);
} catch (Exception& e) {
ImGui::Bullet(); ImGui::Text("after is invalid: %s", e.msg().c_str());
err = true;
}
if (!err) {
if (ImGui::Button("ok")) {
submit = true;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"rename '%s' to '%s' on '%s'",
rename_target_.c_str(), new_name.c_str(), abspath().Stringify().c_str());
}
}
if (submit) {
ImGui::CloseCurrentPopup();
auto ctx = std::make_shared<nf7::GenericContext>(*this, "renaming item");
auto task = [this, before = std::move(rename_target_), after = std::move(new_name)]() {
auto f = Remove(before);
if (!f) throw Exception("missing target");
Add(after, std::move(f));
};
env().ExecMain(ctx, std::move(task));
}
ImGui::EndPopup();
}
// tree view window
const auto kInit = [em]() {
ImGui::SetNextWindowSize({8*em, 8*em}, ImGuiCond_FirstUseEver);
@ -267,6 +256,12 @@ void Dir::UpdateTree() noexcept {
opened_.erase(name);
}
if (ditem && (ditem->flags() & DirItem::kWidget)) {
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
widget_popup_.Open(file);
}
}
// tooltip
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
@ -283,6 +278,11 @@ void Dir::UpdateTree() noexcept {
// context menu
if (ImGui::BeginPopupContextItem()) {
if (ditem && (ditem->flags() & DirItem::kWidget)) {
if (ImGui::MenuItem("open widget")) {
widget_popup_.Open(file);
}
}
if (ImGui::MenuItem("copy path")) {
ImGui::SetClipboardText(file.abspath().Stringify().c_str());
}
@ -294,8 +294,7 @@ void Dir::UpdateTree() noexcept {
[this, name]() { Remove(name); });
}
if (ImGui::MenuItem("rename")) {
rename_target_ = name;
popup_ = "RenamePopup";
rename_popup_.Open(name);
}
if (ImGui::MenuItem("renew")) {
@ -309,7 +308,7 @@ void Dir::UpdateTree() noexcept {
ImGui::Separator();
if (ImGui::MenuItem("add new sibling")) {
popup_ = "NewItemPopup";
add_popup_.Open();
}
if (ditem && (ditem->flags() & DirItem::kMenu)) {
@ -356,7 +355,7 @@ void Dir::UpdateTree() noexcept {
}
void Dir::UpdateMenu() noexcept {
if (ImGui::MenuItem("add new child")) {
popup_ = "NewItemPopup";
add_popup_.Open();
}
ImGui::Separator();
ImGui::MenuItem("TreeView", nullptr, &win_.shown());
@ -391,5 +390,83 @@ try {
} catch (nf7::Exception&) {
}
void Dir::WidgetPopup::Update() noexcept {
if (nf7::gui::Popup::Begin()) {
if (auto item = target_->interface<nf7::DirItem>()) {
ImGui::PushID(item);
item->UpdateWidget();
ImGui::PopID();
}
ImGui::EndPopup();
}
}
void Dir::AddPopup::Update() noexcept {
if (nf7::gui::Popup::Begin()) {
ImGui::TextUnformatted("System/Dir: adding new file...");
if (factory_.Update()) {
ImGui::CloseCurrentPopup();
auto& env = owner_->env();
auto ctx = std::make_shared<nf7::GenericContext>(*owner_, "adding new item");
auto task = [this, &env]() { owner_->Add(factory_.name(), factory_.Create(env)); };
env.ExecMain(ctx, std::move(task));
}
ImGui::EndPopup();
}
}
void Dir::RenamePopup::Update() noexcept {
if (nf7::gui::Popup::Begin()) {
ImGui::TextUnformatted("System/Dir: renaming an exsting item...");
ImGui::InputText("before", &before_);
bool submit = false;
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
if (ImGui::InputText("after", &after_, ImGuiInputTextFlags_EnterReturnsTrue)) {
submit = true;
}
bool err = false;
if (!Find(before_)) {
ImGui::Bullet(); ImGui::TextUnformatted("before is invalid: missing target");
err = true;
}
if (Find(after_)) {
ImGui::Bullet(); ImGui::TextUnformatted("after is invalid: duplicated name");
err = true;
}
try {
Path::ValidateTerm(after_);
} catch (Exception& e) {
ImGui::Bullet(); ImGui::Text("after is invalid: %s", e.msg().c_str());
err = true;
}
if (!err) {
if (ImGui::Button("ok")) {
submit = true;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"rename '%s' to '%s' on '%s'",
before_.c_str(), after_.c_str(),
owner_->abspath().Stringify().c_str());
}
}
if (submit) {
ImGui::CloseCurrentPopup();
auto ctx = std::make_shared<nf7::GenericContext>(*owner_, "renaming item");
auto task = [this, before = std::move(before_), after = std::move(after_)]() {
auto f = owner_->Remove(before);
if (!f) throw Exception("missing target");
owner_->Add(after, std::move(f));
};
owner_->env().ExecMain(ctx, std::move(task));
}
ImGui::EndPopup();
}
}
}
} // namespace nf7

View File

@ -44,7 +44,10 @@ class NativeFile final : public File,
}
NativeFile(Env& env, const std::filesystem::path& path = "", std::string_view mode = "") noexcept :
File(kType, env), DirItem(DirItem::kMenu | DirItem::kTooltip),
nf7::File(kType, env),
nf7::DirItem(
nf7::DirItem::kTooltip |
nf7::DirItem::kWidget),
npath_(path), mode_(mode) {
}
@ -59,8 +62,8 @@ class NativeFile final : public File,
}
void Update() noexcept override;
void UpdateMenu() noexcept override;
void UpdateTooltip() noexcept override;
void UpdateWidget() noexcept override;
void Handle(const Event& ev) noexcept override {
switch (ev.type) {
@ -82,8 +85,6 @@ class NativeFile final : public File,
private:
std::shared_ptr<nf7::AsyncBufferAdaptor> buf_;
const char* popup_ = nullptr;
// persistent params
std::filesystem::path npath_;
std::string mode_;
@ -114,11 +115,20 @@ void NativeFile::Update() noexcept {
}
} catch (std::filesystem::filesystem_error&) {
}
}
void NativeFile::UpdateTooltip() noexcept {
ImGui::Text("basepath: %s", env().npath().generic_string().c_str());
ImGui::Text("path : %s", npath_.generic_string().c_str());
ImGui::Text("mode : %s", mode_.c_str());
}
void NativeFile::UpdateWidget() noexcept {
ImGui::TextUnformatted("System/NativeFile");
if (const auto popup = std::exchange(popup_, nullptr)) {
ImGui::OpenPopup(popup);
if (ImGui::Button("change referencee")) {
ImGui::OpenPopup("ReplaceReferenceePopup");
}
if (ImGui::BeginPopup("ConfigPopup")) {
if (ImGui::BeginPopup("ReplaceReferenceePopup")) {
static std::string path;
static bool flag_exlock;
static bool flag_readable;
@ -161,16 +171,6 @@ void NativeFile::Update() noexcept {
ImGui::EndPopup();
}
}
void NativeFile::UpdateMenu() noexcept {
if (ImGui::MenuItem("config")) {
popup_ = "ConfigPopup";
}
}
void NativeFile::UpdateTooltip() noexcept {
ImGui::Text("basepath: %s", env().npath().generic_string().c_str());
ImGui::Text("path : %s", npath_.generic_string().c_str());
ImGui::Text("mode : %s", mode_.c_str());
}
}
} // namespace nf7