add Node/Imm
This commit is contained in:
parent
5441846713
commit
248405e722
@ -59,6 +59,7 @@ target_sources(nf7
|
||||
common/generic_watcher.hh
|
||||
common/gui_file.hh
|
||||
common/gui_node.hh
|
||||
common/gui_resizer.hh
|
||||
common/gui_window.hh
|
||||
common/history.hh
|
||||
common/lambda.hh
|
||||
@ -88,6 +89,7 @@ target_sources(nf7
|
||||
file/luajit_context.cc
|
||||
file/luajit_node.cc
|
||||
file/luajit_obj.cc
|
||||
file/node_imm.cc
|
||||
file/node_network.cc
|
||||
file/system_dir.cc
|
||||
file/system_imgui_config.cc
|
||||
|
@ -16,10 +16,11 @@ class GenericMemento : public Memento {
|
||||
class CustomTag;
|
||||
|
||||
GenericMemento(File& owner, T&& data) noexcept :
|
||||
owner_(&owner), data_(std::move(data)) {
|
||||
owner_(&owner), initial_(T(data)), data_(std::move(data)) {
|
||||
}
|
||||
~GenericMemento() noexcept {
|
||||
tag_ = nullptr;
|
||||
tag_ = nullptr;
|
||||
last_ = nullptr;
|
||||
assert(map_.empty());
|
||||
}
|
||||
|
||||
@ -27,7 +28,7 @@ class GenericMemento : public Memento {
|
||||
if (tag_) return tag_;
|
||||
auto [itr, emplaced] = map_.emplace(next_++, data_);
|
||||
assert(emplaced);
|
||||
return tag_ = std::make_shared<CustomTag>(*this, itr->first);
|
||||
return last_ = tag_ = std::make_shared<CustomTag>(*this, itr->first);
|
||||
}
|
||||
void Restore(const std::shared_ptr<Tag>& tag) override {
|
||||
assert(tag);
|
||||
@ -35,7 +36,7 @@ class GenericMemento : public Memento {
|
||||
assert(itr != map_.end());
|
||||
data_ = itr->second;
|
||||
tag_ = tag;
|
||||
assert(tag_);
|
||||
last_ = tag;
|
||||
|
||||
if (owner_->id()) {
|
||||
owner_->env().Handle(
|
||||
@ -57,15 +58,25 @@ class GenericMemento : public Memento {
|
||||
T& data() noexcept { return data_; }
|
||||
const T& data() const noexcept { return data_; }
|
||||
|
||||
const T& last() const noexcept {
|
||||
if (!last_) return initial_;
|
||||
|
||||
auto itr = map_.find(last_->id());
|
||||
assert(itr != map_.end());
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
private:
|
||||
File* const owner_;
|
||||
|
||||
const T initial_;
|
||||
T data_;
|
||||
|
||||
Tag::Id next_ = 0;
|
||||
std::unordered_map<Tag::Id, T> map_;
|
||||
|
||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
||||
std::shared_ptr<nf7::Memento::Tag> last_;
|
||||
|
||||
void NotifyUpdate() noexcept {
|
||||
if (owner_->id()) {
|
||||
|
49
common/gui_resizer.hh
Normal file
49
common/gui_resizer.hh
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
namespace nf7::gui {
|
||||
|
||||
bool Resizer(ImVec2* size, const ImVec2& min, const ImVec2& max, float scale,
|
||||
const char* idstr = "##resizer") noexcept {
|
||||
const auto id = ImGui::GetID(idstr);
|
||||
|
||||
size->x = std::clamp(size->x, min.x, max.x);
|
||||
size->y = std::clamp(size->y, min.y, max.y);
|
||||
|
||||
auto ctx = ImGui::GetCurrentContext();
|
||||
auto& io = ImGui::GetIO();
|
||||
|
||||
const auto base = ImGui::GetCursorScreenPos();
|
||||
const auto pos = base + *size*scale;
|
||||
const auto rc = ImRect {pos.x-1*scale, pos.y-1*scale, pos.x, pos.y};
|
||||
|
||||
bool hovered, held;
|
||||
const bool ret = ImGui::ButtonBehavior(rc, id, &hovered, &held,
|
||||
ImGuiButtonFlags_FlattenChildren |
|
||||
ImGuiButtonFlags_PressedOnClickRelease);
|
||||
|
||||
if (hovered || held) ctx->MouseCursor = ImGuiMouseCursor_ResizeNESW;
|
||||
|
||||
ImGuiCol col = ImGuiCol_Button;
|
||||
if (hovered) col = ImGuiCol_ButtonHovered;
|
||||
if (held) {
|
||||
col = ImGuiCol_ButtonActive;
|
||||
*size = (io.MousePos + (ImVec2{scale, scale}-ctx->ActiveIdClickOffset) - base) / scale;
|
||||
size->x = std::clamp(size->x, min.x, max.x);
|
||||
size->y = std::clamp(size->y, min.y, max.y);
|
||||
}
|
||||
|
||||
const auto newpos = base + *size*scale;
|
||||
|
||||
auto dlist = ImGui::GetWindowDrawList();
|
||||
dlist->AddTriangleFilled(
|
||||
newpos, newpos+ImVec2{0, -scale}, newpos+ImVec2{-scale, 0},
|
||||
ImGui::GetColorU32(col));
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace nf7::gui
|
@ -9,6 +9,7 @@
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/string_view.hpp>
|
||||
#include <yas/types/std/variant.hpp>
|
||||
#include <yas/types/utility/usertype.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
@ -18,11 +19,14 @@
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Value final {
|
||||
class Value {
|
||||
public:
|
||||
using IncompatibleException = std::bad_variant_access;
|
||||
class IncompatibleException : public nf7::Exception {
|
||||
public:
|
||||
using nf7::Exception::Exception;
|
||||
};
|
||||
|
||||
class Pulse final { };
|
||||
class Pulse { };
|
||||
class Data;
|
||||
|
||||
using Boolean = bool;
|
||||
@ -38,6 +42,23 @@ class Value final {
|
||||
Value& operator=(const Value&) = default;
|
||||
Value& operator=(Value&&) = default;
|
||||
|
||||
Value(Pulse v) noexcept : value_(v) { }
|
||||
Value& operator=(Pulse v) noexcept { value_ = v; return *this; }
|
||||
Value(Integer v) noexcept : value_(v) { }
|
||||
Value& operator=(Integer v) noexcept { value_ = v; return *this; }
|
||||
Value(Scalar v) noexcept : value_(v) { }
|
||||
Value& operator=(Scalar v) noexcept { value_ = v; return *this; }
|
||||
Value(Boolean v) noexcept : value_(v) { }
|
||||
Value& operator=(Boolean v) noexcept { value_ = v; return *this; }
|
||||
Value(std::string_view v) noexcept : value_(std::string(v)) { }
|
||||
Value& operator=(std::string_view v) noexcept { value_ = std::string(v); return *this; }
|
||||
Value(String&& v) noexcept : value_(std::move(v)) { }
|
||||
Value& operator=(String&& v) noexcept { value_ = std::move(v); return *this; }
|
||||
Value(const DataPtr& v) noexcept : value_(v) { }
|
||||
Value& operator=(const DataPtr& v) noexcept { value_ = v; return *this; }
|
||||
Value(DataPtr&& v) noexcept : value_(std::move(v)) { }
|
||||
Value& operator=(DataPtr&& v) noexcept { value_ = std::move(v); return *this; }
|
||||
|
||||
bool isPulse() const noexcept { return std::holds_alternative<Pulse>(value_); }
|
||||
bool isBoolean() const noexcept { return std::holds_alternative<Boolean>(value_); }
|
||||
bool isInteger() const noexcept { return std::holds_alternative<Integer>(value_); }
|
||||
@ -51,6 +72,12 @@ class Value final {
|
||||
const String& string() const { return std::get<String>(value_); }
|
||||
const DataPtr& data() const { return std::get<DataPtr>(value_); }
|
||||
|
||||
Integer& integer() { return std::get<Integer>(value_); }
|
||||
Boolean& boolean() { return std::get<Boolean>(value_); }
|
||||
Scalar& scalar() { return std::get<Scalar>(value_); }
|
||||
String& string() { return std::get<String>(value_); }
|
||||
DataPtr& data() { return std::get<DataPtr>(value_); }
|
||||
|
||||
const char* typeName() const noexcept {
|
||||
struct Visitor final {
|
||||
public:
|
||||
@ -114,11 +141,11 @@ struct serializer<
|
||||
nf7::Value::DataPtr> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const nf7::Value::DataPtr&) {
|
||||
static Archive& save(Archive&, const nf7::Value::DataPtr&) {
|
||||
throw nf7::Exception("cannot serialize Value::DataPtr");
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, nf7::Value::DataPtr&) {
|
||||
static Archive& load(Archive&, nf7::Value::DataPtr&) {
|
||||
throw nf7::DeserializeException("cannot deserialize Value::DataPtr");
|
||||
}
|
||||
};
|
||||
|
234
file/node_imm.cc
Normal file
234
file/node_imm.cc
Normal file
@ -0,0 +1,234 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <ImNodes.h>
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/string_view.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/gui_node.hh"
|
||||
#include "common/gui_resizer.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/value.hh"
|
||||
#include "common/yas_imgui.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Imm final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
public:
|
||||
static inline const GenericTypeInfo<Imm> kType =
|
||||
{"Node/Imm", {"DirItem", "Node"}};
|
||||
|
||||
class Lambda;
|
||||
|
||||
enum Type { kPulse, kInteger, kScalar, kScalarNormal, kStringText, };
|
||||
static inline const std::vector<std::pair<Type, const char*>> kTypeNames = {
|
||||
{kPulse, "pulse"},
|
||||
{kInteger, "integer"},
|
||||
{kScalar, "scalar"},
|
||||
{kScalarNormal, "scalar/normal"},
|
||||
{kStringText, "string/text"},
|
||||
};
|
||||
|
||||
Imm(Env& env, Type type = kPulse, nf7::Value&& v = {}) noexcept :
|
||||
File(kType, env), DirItem(DirItem::kNone), mem_(*this, {type, std::move(v)}) {
|
||||
output_ = {"out"};
|
||||
}
|
||||
|
||||
Imm(Env& env, Deserializer& ar) : Imm(env) {
|
||||
auto& data = mem_.data();
|
||||
|
||||
std::string typestr;
|
||||
ar(typestr, data.value, data.size);
|
||||
|
||||
ChangeType(ParseType(typestr));
|
||||
mem_.CommitAmend();
|
||||
}
|
||||
void Serialize(Serializer& ar) const noexcept override {
|
||||
const auto& data = mem_.data();
|
||||
ar(std::string_view{StringifyType(data.type)}, data.value, data.size);
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
const auto& data = mem_.data();
|
||||
return std::make_unique<Imm>(env, data.type, nf7::Value{data.value});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Lambda> CreateLambda() noexcept override;
|
||||
|
||||
void UpdateNode(Node::Editor&) noexcept override;
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<
|
||||
nf7::DirItem, nf7::Memento, nf7::Node>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Data final {
|
||||
public:
|
||||
Data(Type t, nf7::Value&& v) noexcept : type(t), value(std::move(v)) {
|
||||
}
|
||||
Type type;
|
||||
nf7::Value value;
|
||||
ImVec2 size;
|
||||
};
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
|
||||
void UpdateEditor(Node::Editor&, float w) noexcept;
|
||||
void ChangeType(Type) noexcept;
|
||||
|
||||
static const char* StringifyType(Type t) noexcept {
|
||||
auto itr = std::find_if(kTypeNames.begin(), kTypeNames.end(),
|
||||
[t](auto& x) { return x.first == t; });
|
||||
assert(itr != kTypeNames.end());
|
||||
return itr->second;
|
||||
}
|
||||
static Type ParseType(std::string_view v) {
|
||||
auto itr = std::find_if(kTypeNames.begin(), kTypeNames.end(),
|
||||
[v](auto& x) { return x.second == v; });
|
||||
if (itr == kTypeNames.end()) {
|
||||
throw nf7::DeserializeException("unknown Node/Imm type");
|
||||
}
|
||||
return itr->first;
|
||||
}
|
||||
};
|
||||
|
||||
class Imm::Lambda final : public nf7::Lambda,
|
||||
public std::enable_shared_from_this<Imm::Lambda> {
|
||||
public:
|
||||
Lambda(Imm& owner) noexcept :
|
||||
nf7::Lambda(owner), value_(owner.mem_.data().value) {
|
||||
}
|
||||
|
||||
void Init(const std::shared_ptr<nf7::Lambda>& parent) noexcept override {
|
||||
parent->Handle(0, nf7::Value{value_}, shared_from_this());
|
||||
}
|
||||
void Handle(size_t, nf7::Value&&, const std::shared_ptr<nf7::Lambda>&) noexcept override {
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Value value_;
|
||||
};
|
||||
|
||||
std::shared_ptr<nf7::Lambda> Imm::CreateLambda() noexcept {
|
||||
return std::make_shared<Imm::Lambda>(*this);
|
||||
}
|
||||
|
||||
|
||||
void Imm::UpdateNode(Node::Editor& editor) noexcept {
|
||||
const auto& style = ImGui::GetStyle();
|
||||
|
||||
auto& data = mem_.data();
|
||||
|
||||
const auto left = ImGui::GetCursorPosX();
|
||||
ImGui::TextUnformatted("Node/Imm");
|
||||
ImGui::SameLine();
|
||||
ImGui::SmallButton(StringifyType(data.type));
|
||||
if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
for (const auto& t : kTypeNames) {
|
||||
if (ImGui::MenuItem(t.second, nullptr, t.first == data.type)) {
|
||||
if (t.first != data.type) {
|
||||
ChangeType(t.first);
|
||||
mem_.Commit();
|
||||
Touch();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
const auto right = ImGui::GetCursorPosX() - style.ItemSpacing.x;
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::PushItemWidth(right-left);
|
||||
if (ImNodes::BeginOutputSlot("out", 1)) {
|
||||
UpdateEditor(editor, right-left);
|
||||
ImGui::SameLine();
|
||||
gui::NodeSocket();
|
||||
ImNodes::EndSlot();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
void Imm::UpdateEditor(Node::Editor&, float w) noexcept {
|
||||
static const double kZero = 0., kOne = 1.;
|
||||
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
bool mod = false, com = false;
|
||||
|
||||
auto& d = mem_.data();
|
||||
auto& v = d.value;
|
||||
switch (d.type) {
|
||||
case kPulse:
|
||||
ImGui::Dummy({w, em});
|
||||
break;
|
||||
case kInteger:
|
||||
mod = ImGui::DragScalar("##integer", ImGuiDataType_S64, &v.integer());
|
||||
com = ImGui::IsItemDeactivatedAfterEdit();
|
||||
break;
|
||||
case kScalar:
|
||||
mod = ImGui::DragScalar("##scalar", ImGuiDataType_Double, &v.scalar());
|
||||
com = ImGui::IsItemDeactivatedAfterEdit();
|
||||
break;
|
||||
case kScalarNormal:
|
||||
mod = ImGui::SliderScalar("##scalar_normal", ImGuiDataType_Double, &v.scalar(), &kZero, &kOne);
|
||||
com = ImGui::IsItemDeactivatedAfterEdit();
|
||||
break;
|
||||
case kStringText:
|
||||
if (gui::Resizer(&d.size, {w/em, 3}, {128, 128}, em)) {
|
||||
const auto& psize = mem_.last().size;
|
||||
mod |= psize.x != d.size.x || psize.y != d.size.y;
|
||||
com |= mod;
|
||||
}
|
||||
mod |= ImGui::InputTextMultiline("##string_text", &v.string(), d.size*em);
|
||||
com |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
break;
|
||||
}
|
||||
|
||||
if (com) {
|
||||
mem_.Commit();
|
||||
} else if (mod) {
|
||||
Touch();
|
||||
}
|
||||
}
|
||||
void Imm::ChangeType(Type t) noexcept {
|
||||
auto& d = mem_.data();
|
||||
d.type = t;
|
||||
switch (d.type) {
|
||||
case kPulse:
|
||||
d.value = nf7::Value::Pulse{};
|
||||
break;
|
||||
case kInteger:
|
||||
if (!d.value.isInteger()) {
|
||||
d.value = nf7::Value::Integer{0};
|
||||
}
|
||||
break;
|
||||
case kScalar:
|
||||
case kScalarNormal:
|
||||
if (!d.value.isScalar()) {
|
||||
d.value = nf7::Value::Scalar{0.};
|
||||
}
|
||||
break;
|
||||
case kStringText:
|
||||
if (!d.value.isString()) {
|
||||
d.value = nf7::Value::String{""};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nf7
|
Loading…
Reference in New Issue
Block a user