add nf7::gui::Config

This commit is contained in:
falsycat 2022-09-26 11:12:18 +09:00
parent d0d6a2ebd5
commit 8688ef98b6
7 changed files with 179 additions and 165 deletions

View File

@ -77,6 +77,7 @@ target_sources(nf7
common/generic_type_info.hh
common/generic_watcher.hh
common/gui_dnd.hh
common/gui_config.hh
common/gui_context.hh
common/gui_file.hh
common/gui_file.cc

59
common/gui_config.hh Normal file
View File

@ -0,0 +1,59 @@
#include <string>
#include <type_traits>
#include <imgui.h>
#include <imgui_stdlib.h>
#include "nf7.hh"
#include "common/generic_memento.hh"
namespace nf7::gui {
template <typename T>
concept ConfigData = requires (T& x) {
{ x.Stringify() } -> std::convertible_to<std::string>;
x.Parse(std::string {});
};
template <ConfigData T>
void Config(nf7::GenericMemento<T>& mem) noexcept {
static std::string text_;
static std::string msg_;
static bool mod_;
if (ImGui::IsWindowAppearing()) {
text_ = mem->Stringify();
msg_ = "";
mod_ = false;
}
mod_ |= ImGui::InputTextMultiline("##config", &text_);
ImGui::BeginDisabled(!mod_);
if (ImGui::Button("apply")) {
try {
mem->Parse(text_);
mem.Commit();
msg_ = "";
mod_ = false;
} catch (nf7::Exception& e) {
msg_ = e.msg();
}
}
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("restore")) {
text_ = mem->Stringify();
msg_ = "";
mod_ = false;
}
if (msg_.size()) {
ImGui::Bullet();
ImGui::TextUnformatted(msg_.c_str());
}
}
} // namespace nf7::gui

View File

@ -45,31 +45,4 @@ void IOSocketListPopup::Update() noexcept {
}
}
void ConfigPopup::Update() noexcept {
if (nf7::gui::Popup::Begin()) {
ImGui::TextUnformatted(name());
ImGui::InputTextMultiline("##text", &text_);
if (ImGui::Button("apply")) {
try {
onApply(text_);
msg_ = "OK";
} catch (nf7::Exception& e) {
msg_ = e.msg();
}
}
ImGui::SameLine();
if (ImGui::Button("reset")) {
text_ = onOpen();
}
if (msg_.size()) {
ImGui::Bullet();
ImGui::TextUnformatted(msg_.c_str());
}
ImGui::EndPopup();
}
}
} // namespace nf7::gui

View File

@ -64,26 +64,4 @@ class IOSocketListPopup final :
std::string is_, os_;
};
class ConfigPopup final :
public nf7::FileBase::Feature, private Popup {
public:
ConfigPopup(const char* name = "ConfigPopup") noexcept : Popup(name) {
}
void Open() noexcept {
msg_ = "";
text_ = onOpen();
nf7::gui::Popup::Open();
}
void Update() noexcept override;
std::function<std::string()> onOpen;
std::function<void(const std::string&)> onApply;
private:
std::string msg_;
std::string text_;
};
} // namespace nf7::gui

View File

@ -8,6 +8,8 @@
#include <ImNodes.h>
#include <yaml-cpp/yaml.h>
#include <yas/serialize.hpp>
#include <yas/types/std/string.hpp>
@ -17,9 +19,9 @@
#include "common/file_base.hh"
#include "common/generic_type_info.hh"
#include "common/generic_memento.hh"
#include "common/gui_config.hh"
#include "common/gui_file.hh"
#include "common/gui_node.hh"
#include "common/gui_popup.hh"
#include "common/life.hh"
#include "common/logger_ref.hh"
#include "common/luajit_queue.hh"
@ -28,6 +30,7 @@
#include "common/memento.hh"
#include "common/node.hh"
#include "common/ptr_selector.hh"
#include "common/util_algorithm.hh"
using namespace std::literals;
@ -41,15 +44,15 @@ class InlineNode final : public nf7::FileBase, public nf7::DirItem, public nf7::
static inline const nf7::GenericTypeInfo<InlineNode> kType =
{"LuaJIT/InlineNode", {"nf7::DirItem", "nf7::Node"}};
static void UpdateTypeTooltip() noexcept {
ImGui::TextUnformatted("Defines new Node using Lua object factory.");
ImGui::Bullet();
ImGui::TextUnformatted("refers nf7::luajit::Queue through linked LuaJIT/Obj");
ImGui::TextUnformatted("Defines new pure Node without creating nfile.");
}
class Lambda;
struct Data {
Data() noexcept { }
std::string Stringify() const noexcept;
void Parse(const std::string&);
std::string script;
std::vector<std::string> inputs = {"in"};
@ -57,39 +60,35 @@ 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::FileBase(kType, env, {}),
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kCustomNode),
life_(*this),
log_(std::make_shared<nf7::LoggerRef>(*this)),
mem_(std::move(data), *this) {
nf7::FileBase::Install(*log_);
socket_popup_.onSubmit = [this](auto&& i, auto&& o) {
this->data().inputs = std::move(i);
this->data().outputs = std::move(o);
mem_.Commit();
};
}
InlineNode(nf7::Deserializer& ar) : InlineNode(ar.env()) {
ar(data().script, data().inputs, data().outputs);
ar(mem_->script, mem_->inputs, mem_->outputs);
nf7::util::Uniq(mem_->inputs);
nf7::util::Uniq(mem_->outputs);
}
void Serialize(nf7::Serializer& ar) const noexcept override {
ar(data().script, data().inputs, data().outputs);
ar(mem_->script, mem_->inputs, mem_->outputs);
}
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
return std::make_unique<InlineNode>(env, Data {data()});
return std::make_unique<InlineNode>(env, Data {mem_.data()});
}
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override;
std::span<const std::string> GetInputs() const noexcept override {
return data().inputs;
return mem_->inputs;
}
std::span<const std::string> GetOutputs() const noexcept override {
return data().outputs;
return mem_->outputs;
}
void UpdateMenu() noexcept override;
@ -107,12 +106,9 @@ class InlineNode final : public nf7::FileBase, public nf7::DirItem, public nf7::
std::shared_ptr<nf7::LoggerRef> log_;
nf7::GenericMemento<Data> mem_;
const Data& data() const noexcept { return mem_.data(); }
Data& data() noexcept { return mem_.data(); }
nf7::gui::IOSocketListPopup socket_popup_;
};
class InlineNode::Lambda final : public nf7::Node::Lambda,
public std::enable_shared_from_this<InlineNode::Lambda> {
public:
@ -212,16 +208,16 @@ class InlineNode::Lambda final : public nf7::Node::Lambda,
std::optional<nf7::luajit::Ref> func_;
std::optional<nf7::luajit::Ref> ctxtable_;
};
std::shared_ptr<nf7::Node::Lambda> InlineNode::CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
return std::make_shared<Lambda>(*this, parent);
}
void InlineNode::UpdateMenu() noexcept {
if (ImGui::MenuItem("I/O list")) {
socket_popup_.Open(data().inputs, data().outputs);
if (ImGui::BeginMenu("config")) {
nf7::gui::Config(mem_);
ImGui::EndMenu();
}
}
void InlineNode::UpdateNode(nf7::Node::Editor&) noexcept {
@ -229,32 +225,51 @@ void InlineNode::UpdateNode(nf7::Node::Editor&) noexcept {
ImGui::TextUnformatted("LuaJIT/InlineNode");
ImGui::SameLine();
if (ImGui::SmallButton("I/O list")) {
socket_popup_.Open(data().inputs, data().outputs);
if (ImGui::SmallButton("config")) {
ImGui::OpenPopup("ConfigPopup");
}
if (ImGui::BeginPopup("ConfigPopup")) {
nf7::gui::Config(mem_);
ImGui::EndPopup();
}
nf7::gui::NodeInputSockets(data().inputs);
nf7::gui::NodeInputSockets(mem_->inputs);
ImGui::SameLine();
ImGui::InputTextMultiline("##script", &data().script, {24*em, 8*em});
ImGui::InputTextMultiline("##script", &mem_->script, {24*em, 8*em});
if (ImGui::IsItemDeactivatedAfterEdit()) {
mem_.Commit();
}
ImGui::SameLine();
nf7::gui::NodeOutputSockets(data().outputs);
socket_popup_.Update();
nf7::gui::NodeOutputSockets(mem_->outputs);
}
void InlineNode::UpdateWidget() noexcept {
ImGui::TextUnformatted("LuaJIT/InlineNode");
if (ImGui::Button("I/O list")) {
socket_popup_.Open(data().inputs, data().outputs);
}
ImGui::InputTextMultiline("script", &data().script);
if (ImGui::IsItemDeactivatedAfterEdit()) {
mem_.Commit();
}
nf7::gui::Config(mem_);
}
socket_popup_.Update();
std::string InlineNode::Data::Stringify() const noexcept {
YAML::Emitter st;
st << YAML::BeginMap;
st << YAML::Key << "inputs";
st << YAML::Value << inputs;
st << YAML::Key << "outputs";
st << YAML::Value << outputs;
st << YAML::Key << "script";
st << YAML::Value << YAML::Literal << script;
st << YAML::EndMap;
return std::string {st.c_str(), st.size()};
}
void InlineNode::Data::Parse(const std::string& str)
try {
const auto yaml = YAML::Load(str);
auto new_inputs = yaml["inputs"] .as<std::vector<std::string>>();
auto new_outputs = yaml["outputs"].as<std::vector<std::string>>();
auto new_script = yaml["script"].as<std::string>();
inputs = std::move(new_inputs);
outputs = std::move(new_outputs);
script = std::move(new_script);
} catch (YAML::Exception& e) {
throw nf7::Exception {e.what()};
}
}

View File

@ -12,6 +12,8 @@
#include <lua.hpp>
#include <yaml-cpp/yaml.h>
#include <yas/serialize.hpp>
#include "nf7.hh"
@ -22,6 +24,7 @@
#include "common/generic_context.hh"
#include "common/generic_type_info.hh"
#include "common/generic_memento.hh"
#include "common/gui_config.hh"
#include "common/life.hh"
#include "common/logger_ref.hh"
#include "common/luajit.hh"
@ -52,11 +55,13 @@ class LuaNode final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
class Lambda;
struct Meta {
public:
std::vector<std::string> inputs, outputs;
std::optional<nf7::luajit::Ref> lambda;
};
struct Data {
std::string Stringify() const noexcept;
void Parse(const std::string&);
std::filesystem::path npath;
};
@ -283,26 +288,24 @@ void LuaNode::UpdateTooltip() noexcept {
}
}
void LuaNode::UpdateWidget() noexcept {
if (ImGui::Button("change nfile path")) {
ImGui::OpenPopup("NPathPopup");
}
nf7::gui::Config(mem_);
}
if (ImGui::BeginPopup("NPathPopup")) {
static std::string npath;
if (ImGui::IsWindowAppearing()) {
npath = mem_->npath.string();
}
const bool submit =
ImGui::InputText("npath", &npath, ImGuiInputTextFlags_EnterReturnsTrue);
if (ImGui::Button("ok") || submit) {
ImGui::CloseCurrentPopup();
mem_->npath = npath;
mem_.Commit();
nfile_watcher_.Watch(env().npath() / npath);
Build();
}
ImGui::EndPopup();
}
std::string LuaNode::Data::Stringify() const noexcept {
YAML::Emitter st;
st << YAML::BeginMap;
st << YAML::Key << "npath";
st << YAML::Value << npath.string();
st << YAML::EndMap;
return std::string {st.c_str(), st.size()};
}
void LuaNode::Data::Parse(const std::string& str)
try {
const auto yaml = YAML::Load(str);
npath = yaml["npath"].as<std::string>();
} catch (YAML::Exception& e) {
throw nf7::Exception {e.what()};
}
}

View File

@ -23,8 +23,8 @@
#include "common/file_base.hh"
#include "common/generic_memento.hh"
#include "common/generic_type_info.hh"
#include "common/gui_config.hh"
#include "common/gui_node.hh"
#include "common/gui_popup.hh"
#include "common/gui_window.hh"
#include "common/life.hh"
#include "common/logger_ref.hh"
@ -50,6 +50,8 @@ class Plot final : public nf7::FileBase,
ImGui::Bullet(); ImGui::TextUnformatted("implements nf7::Node");
}
class Lambda;
enum SeriesType {
kLine,
kScatter,
@ -102,40 +104,30 @@ class Plot final : public nf7::FileBase,
void Update() const noexcept;
};
struct Data {
std::vector<Series> series;
std::string Stringify() const noexcept;
static Data Parse(const std::string&);
void Parse(const std::string&);
std::vector<Series> series;
};
class Lambda;
Plot(nf7::Env& env, const nf7::gui::Window* win = nullptr, Data&& data = {}) noexcept :
nf7::FileBase(kType, env, {&log_, &config_popup_}),
nf7::FileBase(kType, env, {&log_}),
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kWidget),
nf7::Node(nf7::Node::kNone),
life_(*this), log_(*this), win_(*this, "Plot", win), mem_(std::move(data)),
config_popup_("Value/Plot: config") {
life_(*this), log_(*this), win_(*this, "Plot", win), mem_(std::move(data)) {
mem_.onRestore = mem_.onCommit = [this]() { BuildInputList(); };
config_popup_.onOpen = [this]() { return this->data().Stringify(); };
config_popup_.onApply = [this](auto& str) {
this->data() = Data::Parse(str);
mem_.Commit();
};
Sanitize();
}
Plot(nf7::Deserializer& ar) : Plot(ar.env()) {
ar(win_, data().series);
ar(win_, mem_->series);
Sanitize();
}
void Serialize(nf7::Serializer& ar) const noexcept override {
ar(win_, data().series);
ar(win_, mem_->series);
}
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
return std::make_unique<Plot>(env, &win_, Data {data()});
return std::make_unique<Plot>(env, &win_, Data {mem_.data()});
}
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
@ -166,22 +158,18 @@ class Plot final : public nf7::FileBase,
nf7::gui::Window win_;
nf7::GenericMemento<Data> mem_;
Data& data() noexcept { return mem_.data(); }
const Data& data() const noexcept { return mem_.data(); }
std::vector<std::string> inputs_;
nf7::gui::ConfigPopup config_popup_;
void Sanitize() {
nf7::util::Uniq(data().series);
nf7::util::Uniq(mem_->series);
mem_.CommitAmend();
}
void BuildInputList() {
inputs_.clear();
inputs_.reserve(data().series.size());
for (const auto& s : data().series) {
inputs_.reserve(mem_->series.size());
for (const auto& s : mem_->series) {
inputs_.push_back(s.name);
}
}
@ -199,7 +187,7 @@ class Plot::Lambda final : public nf7::Node::Lambda {
try {
f_.EnforceAlive();
const auto& series = f_->data().series;
const auto& series = f_->mem_->series;
auto itr = std::find(series.begin(), series.end(), k);
if (itr == series.end()) {
throw nf7::Exception {"unknown series name"};
@ -262,17 +250,14 @@ void Plot::UpdateWidget() noexcept {
if (ImGui::Button("plot window")) {
win_.SetFocus();
}
if (ImGui::Button("config")) {
config_popup_.Open();
}
config_popup_.Update();
nf7::gui::Config(mem_);
}
void Plot::UpdateMenu() noexcept {
ImGui::MenuItem("plot window", nullptr, &win_.shown());
if (ImGui::MenuItem("config")) {
config_popup_.Open();
if (ImGui::BeginMenu("config")) {
nf7::gui::Config(mem_);
ImGui::EndMenu();
}
}
@ -280,7 +265,7 @@ void Plot::UpdatePlot() noexcept {
if (ImPlot::BeginPlot("##plot", ImGui::GetContentRegionAvail())) {
ImPlot::SetupAxis(ImAxis_X1, "X", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxis(ImAxis_Y1, "Y", ImPlotAxisFlags_AutoFit);
for (const auto& s : data().series) {
for (const auto& s : mem_->series) {
s.Update();
}
ImPlot::EndPlot();
@ -398,35 +383,35 @@ void Plot::Series::Update() const noexcept {
std::string Plot::Data::Stringify() const noexcept {
YAML::Emitter Y;
Y << YAML::BeginMap;
Y << YAML::Key << "series";
Y << YAML::Value << YAML::BeginMap;
YAML::Emitter st;
st << YAML::BeginMap;
st << YAML::Key << "series";
st << YAML::Value << YAML::BeginMap;
for (auto& s : series) {
Y << YAML::Key << s.name;
Y << YAML::Value << YAML::BeginMap;
Y << YAML::Key << "type";
Y << YAML::Value << std::string {magic_enum::enum_name(s.type)};
Y << YAML::Key << "fmt" ;
Y << YAML::Value << std::string {magic_enum::enum_name(s.fmt)};
Y << YAML::EndMap;
st << YAML::Key << s.name;
st << YAML::Value << YAML::BeginMap;
st << YAML::Key << "type";
st << YAML::Value << std::string {magic_enum::enum_name(s.type)};
st << YAML::Key << "fmt" ;
st << YAML::Value << std::string {magic_enum::enum_name(s.fmt)};
st << YAML::EndMap;
}
Y << YAML::EndMap;
Y << YAML::EndMap;
return std::string {Y.c_str(), Y.size()};
st << YAML::EndMap;
st << YAML::EndMap;
return std::string {st.c_str(), st.size()};
}
Plot::Data Plot::Data::Parse(const std::string& str)
void Plot::Data::Parse(const std::string& str)
try {
const auto& root = YAML::Load(str);
const auto& yaml = YAML::Load(str);
Data d;
for (auto& s : root["series"]) {
d.series.emplace_back(
std::vector<Series> new_series;
for (auto& s : yaml["series"]) {
new_series.emplace_back(
s.first.as<std::string>(),
magic_enum::enum_cast<SeriesType>(s.second["type"].as<std::string>()).value(),
magic_enum::enum_cast<SeriesFormat>(s.second["fmt"].as<std::string>()).value());
}
return d;
series = std::move(new_series);
} catch (std::bad_optional_access&) {
throw nf7::Exception {"unknown enum"};
} catch (YAML::Exception& e) {