add Value/Imm to replace Node/Imm
This commit is contained in:
parent
eeb7dabd1f
commit
49565e657c
@ -162,6 +162,7 @@ target_sources(nf7
|
||||
file/system_nfile.cc
|
||||
file/system_node.cc
|
||||
file/value_curve.cc
|
||||
file/value_imm.cc
|
||||
file/value_plot.cc
|
||||
)
|
||||
target_link_libraries(nf7
|
||||
|
520
file/value_imm.cc
Normal file
520
file/value_imm.cc
Normal file
@ -0,0 +1,520 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_stdlib.h>
|
||||
|
||||
#include <ImNodes.h>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/array.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/variant.hpp>
|
||||
#include <yas/types/utility/usertype.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/gui.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/value.hh"
|
||||
#include "common/yas_imgui.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
struct EditorStatus {
|
||||
// input
|
||||
const bool emittable;
|
||||
const bool autosize;
|
||||
|
||||
// output
|
||||
bool mod = false;
|
||||
std::optional<nf7::Value> emit = {};
|
||||
};
|
||||
|
||||
|
||||
struct Pulse {
|
||||
public:
|
||||
static constexpr const char* kName = "pulse";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return nf7::Value::Pulse {};
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
ImGui::BeginDisabled(!ed.emittable);
|
||||
if (ImGui::Button("PULSE", {6*ImGui::GetFontSize(), 0})) {
|
||||
ed.emit = nf7::Value {};
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
void serialize(auto&) {
|
||||
}
|
||||
};
|
||||
struct Integer {
|
||||
public:
|
||||
static constexpr const char* kName = "integer";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return {value_};
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
if (!ed.autosize) {
|
||||
ImGui::SetNextItemWidth(6*ImGui::GetFontSize());
|
||||
}
|
||||
if (ImGui::DragScalar("##value", ImGuiDataType_S64, &value_)) {
|
||||
ed.emit = nf7::Value {value_};
|
||||
}
|
||||
ed.mod = ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
void serialize(auto& ar) {
|
||||
ar(value_);
|
||||
}
|
||||
private:
|
||||
nf7::Value::Integer value_ = 0;
|
||||
};
|
||||
struct Scalar {
|
||||
public:
|
||||
static constexpr const char* kName = "scalar";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return nf7::Value {value_};
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
if (!ed.autosize) {
|
||||
ImGui::SetNextItemWidth(6*ImGui::GetFontSize());
|
||||
}
|
||||
if (ImGui::DragScalar("##value", ImGuiDataType_Double, &value_)) {
|
||||
ed.emit = nf7::Value {value_};
|
||||
}
|
||||
ed.mod = ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
void serialize(auto& ar) {
|
||||
ar(value_);
|
||||
}
|
||||
private:
|
||||
nf7::Value::Scalar value_ = 0;
|
||||
};
|
||||
struct String {
|
||||
public:
|
||||
static constexpr const char* kName = "string";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return nf7::Value {value_};
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
if (!ed.autosize) {
|
||||
ImGui::SetNextItemWidth(6*ImGui::GetFontSize());
|
||||
}
|
||||
ImGui::InputTextMultiline("##value", &value_, {0, 2.4f*em});
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
ed.emit = GetValue();
|
||||
ed.mod = true;
|
||||
}
|
||||
}
|
||||
void serialize(auto& ar) {
|
||||
ar(value_);
|
||||
}
|
||||
private:
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
|
||||
template <double kMin, double kMax>
|
||||
struct SliderBase {
|
||||
public:
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return nf7::Value {value_};
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
static const double max = kMax;
|
||||
static const double min = kMin;
|
||||
|
||||
if (!ed.autosize) {
|
||||
ImGui::SetNextItemWidth(8*ImGui::GetFontSize());
|
||||
}
|
||||
if (ImGui::SliderScalar("##value", ImGuiDataType_Double, &value_, &min, &max)) {
|
||||
ed.emit = nf7::Value {value_};
|
||||
}
|
||||
ed.mod = ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
void serialize(auto& ar) {
|
||||
ar(value_);
|
||||
}
|
||||
private:
|
||||
nf7::Value::Scalar value_ = 0;
|
||||
};
|
||||
struct Slider01 : public SliderBase<0., 1.> {
|
||||
static constexpr const char* kName = "slider 0~1";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
};
|
||||
struct Slider11 : public SliderBase<-1., 1.> {
|
||||
static constexpr const char* kName = "slider -1~1";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
};
|
||||
|
||||
|
||||
struct Color {
|
||||
public:
|
||||
static constexpr const char* kName = "color";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return std::vector<nf7::Value>(values_.begin(), values_.end());
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
if (!ed.autosize) {
|
||||
ImGui::SetNextItemWidth(16*ImGui::GetFontSize());
|
||||
}
|
||||
if (ImGui::ColorEdit4("##value", values_.data())) {
|
||||
ed.emit = GetValue();
|
||||
}
|
||||
ed.mod = ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
void serialize(auto& ar) {
|
||||
ar(values_);
|
||||
}
|
||||
private:
|
||||
std::array<float, 4> values_;
|
||||
};
|
||||
|
||||
struct Pos2D {
|
||||
public:
|
||||
static constexpr const char* kName = "position 2D";
|
||||
static constexpr const char* kDesc = nullptr;
|
||||
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return std::vector<nf7::Value>(values_.begin(), values_.end());
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
const auto& io = ImGui::GetIO();
|
||||
auto dlist = ImGui::GetForegroundDrawList();
|
||||
|
||||
if (!ed.autosize) {
|
||||
ImGui::SetNextItemWidth(6*ImGui::GetFontSize());
|
||||
}
|
||||
ImGui::DragFloat2("##value", values_.data(), 1e-3f);
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("+");
|
||||
if (ImGui::IsItemActive()) {
|
||||
if (ImGui::IsItemActivated()) {
|
||||
prev_[0] = values_[0], prev_[1] = values_[1];
|
||||
std::copy(values_.begin(), values_.end(), prev_.begin());
|
||||
}
|
||||
const auto fg_col = ImGui::GetColorU32(ImGuiCol_DragDropTarget);
|
||||
const auto center = ImGui::GetItemRectMin() + ImGui::GetItemRectSize()/2;
|
||||
const auto mouse = ImGui::GetMousePos();
|
||||
dlist->AddLine(mouse, center, fg_col);
|
||||
|
||||
const auto axis_size = io.KeyCtrl? 32*em: 16*em;
|
||||
const auto axis_col = ImGui::GetColorU32(ImGuiCol_DragDropTarget, 0.4f);
|
||||
dlist->AddLine(center-ImVec2(axis_size, 0),
|
||||
center+ImVec2(axis_size, 0),
|
||||
axis_col);
|
||||
dlist->AddLine(center-ImVec2(0, axis_size),
|
||||
center+ImVec2(0, axis_size),
|
||||
axis_col);
|
||||
|
||||
const auto apos = mouse - center;
|
||||
const auto rad = std::sqrt(apos.x*apos.x + apos.y*apos.y);
|
||||
dlist->AddCircle(center, rad, axis_col);
|
||||
|
||||
// set origin pos to values_
|
||||
const auto rpos = apos / axis_size;
|
||||
if (io.KeyShift) {
|
||||
std::copy(prev_.begin(), prev_.end(), values_.begin());
|
||||
} else {
|
||||
std::fill(values_.begin(), values_.end(), 0.f);
|
||||
}
|
||||
|
||||
// draw origin text
|
||||
const auto origin_str = std::to_string(values_[0])+", "+std::to_string(values_[1]);
|
||||
dlist->AddText(center, axis_col, origin_str.c_str());
|
||||
|
||||
// draw mouse pos
|
||||
const auto mouse_str = std::to_string(rpos.x)+", "+std::to_string(rpos.y);
|
||||
dlist->AddText(mouse, axis_col, mouse_str.c_str());
|
||||
|
||||
// add rpos to values_
|
||||
values_[0] += rpos.x;
|
||||
values_[1] += rpos.y;
|
||||
ed.emit = GetValue();
|
||||
}
|
||||
if (ImGui::IsItemDeactivated()) {
|
||||
ed.mod = !std::equal(values_.begin(), values_.end(), prev_.begin());
|
||||
}
|
||||
}
|
||||
void serialize(auto& ar) {
|
||||
ar(values_);
|
||||
}
|
||||
private:
|
||||
std::array<float, 2> values_;
|
||||
|
||||
std::array<float, 2> prev_;
|
||||
};
|
||||
|
||||
|
||||
class Imm final : public nf7::FileBase,
|
||||
public nf7::DirItem, public nf7::Node {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Imm> kType = {
|
||||
"Value/Imm", {"nf7::DirItem", "nf7::Node"},
|
||||
"immediate value",
|
||||
};
|
||||
|
||||
class NodeLambda;
|
||||
|
||||
using Value = std::variant<
|
||||
Pulse, Integer, Scalar, String, Slider01, Slider11, Pos2D, Color>;
|
||||
struct Data {
|
||||
Value value;
|
||||
bool autoemit;
|
||||
|
||||
void serialize(auto& ar) {
|
||||
ar(value, autoemit);
|
||||
}
|
||||
};
|
||||
|
||||
Imm(nf7::Env& env, Data&& data = {}) noexcept :
|
||||
nf7::FileBase(kType, env),
|
||||
nf7::DirItem(nf7::DirItem::kMenu |
|
||||
nf7::DirItem::kTree |
|
||||
nf7::DirItem::kTooltip),
|
||||
nf7::Node(nf7::Node::kCustomNode),
|
||||
life_(*this), mem_(*this, std::move(data)) {
|
||||
}
|
||||
|
||||
Imm(nf7::Deserializer& ar) : Imm(ar.env()) {
|
||||
ar(mem_.data());
|
||||
}
|
||||
void Serialize(nf7::Serializer& ar) const noexcept override {
|
||||
ar(mem_.data());
|
||||
}
|
||||
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
|
||||
return std::make_unique<Imm>(env, Data {mem_.data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override;
|
||||
nf7::Node::Meta GetMeta() const noexcept override {
|
||||
return {{"in"}, {"out"}};
|
||||
}
|
||||
|
||||
void PostHandle(const nf7::File::Event& e) noexcept override {
|
||||
switch (e.type) {
|
||||
case nf7::File::Event::kAdd:
|
||||
la_node_ = std::make_shared<NodeLambda>(*this);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateNode(nf7::Node::Editor&) noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateTree() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
|
||||
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<
|
||||
nf7::DirItem, nf7::Memento, nf7::Node>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Imm> life_;
|
||||
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
|
||||
std::shared_ptr<NodeLambda> la_node_;
|
||||
|
||||
|
||||
nf7::Value GetValue() const noexcept {
|
||||
return std::visit([](auto& t) { return t.GetValue(); }, mem_->value);
|
||||
}
|
||||
const char* GetTypeName() const noexcept {
|
||||
return std::visit([](auto& t) { return t.kName; }, mem_->value);
|
||||
}
|
||||
void Editor(EditorStatus& ed) noexcept {
|
||||
std::visit([&](auto& t) { t.Editor(ed); }, mem_->value);
|
||||
}
|
||||
|
||||
// widgets
|
||||
void MenuItems() noexcept;
|
||||
template <typename T> void MenuItem() noexcept;
|
||||
};
|
||||
|
||||
class Imm::NodeLambda final : public nf7::Node::Lambda,
|
||||
public std::enable_shared_from_this<NodeLambda> {
|
||||
public:
|
||||
NodeLambda(Imm& f) noexcept : nf7::Node::Lambda(f), f_(f.life_) {
|
||||
}
|
||||
void Handle(const nf7::Node::Lambda::Msg& in) noexcept override {
|
||||
if (f_) {
|
||||
in.sender->Handle("out", f_->GetValue(), shared_from_this());
|
||||
}
|
||||
}
|
||||
private:
|
||||
nf7::Life<Imm>::Ref f_;
|
||||
};
|
||||
std::shared_ptr<nf7::Node::Lambda> Imm::CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept {
|
||||
return la_node_;
|
||||
}
|
||||
|
||||
void Imm::UpdateNode(nf7::Node::Editor& ed) noexcept {
|
||||
ImGui::TextUnformatted("Value/Imm");
|
||||
ImGui::SameLine();
|
||||
ImGui::SmallButton(GetTypeName());
|
||||
if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
MenuItems();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImNodes::BeginInputSlot("in", 1)) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
gui::NodeSocket();
|
||||
ImNodes::EndSlot();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
EditorStatus stat = {
|
||||
.emittable = true,
|
||||
.autosize = false,
|
||||
};
|
||||
Editor(stat);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImNodes::BeginOutputSlot("out", 1)) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
gui::NodeSocket();
|
||||
ImNodes::EndSlot();
|
||||
}
|
||||
|
||||
if (stat.emit) {
|
||||
ed.Emit(*this, "out", std::move(*stat.emit));
|
||||
}
|
||||
if (stat.mod) {
|
||||
mem_.Commit();
|
||||
}
|
||||
}
|
||||
void Imm::UpdateMenu() noexcept {
|
||||
if (ImGui::BeginMenu("type")) {
|
||||
MenuItems();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem("emit on change", nullptr, &mem_->autoemit)) {
|
||||
mem_.Commit();
|
||||
}
|
||||
}
|
||||
void Imm::UpdateTree() noexcept {
|
||||
EditorStatus stat {
|
||||
.emittable = false,
|
||||
.autosize = true,
|
||||
};
|
||||
Editor(stat);
|
||||
if (stat.mod) {
|
||||
mem_.Commit();
|
||||
}
|
||||
}
|
||||
void Imm::UpdateTooltip() noexcept {
|
||||
ImGui::Text("type : %s", GetTypeName());
|
||||
|
||||
ImGui::TextUnformatted("preview:");
|
||||
EditorStatus stat {
|
||||
.emittable = false,
|
||||
.autosize = false,
|
||||
};
|
||||
ImGui::Indent();
|
||||
Editor(stat);
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
void Imm::MenuItems() noexcept {
|
||||
MenuItem<Pulse>();
|
||||
MenuItem<Integer>();
|
||||
MenuItem<Scalar>();
|
||||
MenuItem<String>();
|
||||
ImGui::Separator();
|
||||
MenuItem<Slider01>();
|
||||
MenuItem<Slider11>();
|
||||
ImGui::Separator();
|
||||
MenuItem<Pos2D>();
|
||||
MenuItem<Color>();
|
||||
}
|
||||
template <typename T>
|
||||
void Imm::MenuItem() noexcept {
|
||||
const bool holding = std::holds_alternative<T>(mem_->value);
|
||||
if (ImGui::MenuItem(T::kName, nullptr, holding) && !holding) {
|
||||
mem_->value = T {};
|
||||
mem_.Commit();
|
||||
}
|
||||
if (T::kDesc && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", T::kDesc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
||||
|
||||
|
||||
namespace yas::detail {
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
nf7::Imm::Value> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const nf7::Imm::Value& v) {
|
||||
std::visit([&](auto& v) { ar(std::string_view {v.kName}, v); }, v);
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, nf7::Imm::Value& v) {
|
||||
std::string name;
|
||||
ar(name);
|
||||
LoadVariantType(name, ar, v);
|
||||
return ar;
|
||||
}
|
||||
private:
|
||||
template <size_t kI = 0>
|
||||
static void LoadVariantType(std::string_view name, auto& ar, nf7::Imm::Value& v) {
|
||||
if constexpr (kI < std::variant_size_v<nf7::Imm::Value>) {
|
||||
using T = std::variant_alternative_t<kI, nf7::Imm::Value>;
|
||||
if (name == T::kName) {
|
||||
T data;
|
||||
ar(data);
|
||||
v = std::move(data);
|
||||
} else {
|
||||
LoadVariantType<kI+1>(name, ar, v);
|
||||
}
|
||||
} else {
|
||||
throw nf7::Exception {"unknown Value/Imm type: "+std::string {name}};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace yas::detail
|
Loading…
x
Reference in New Issue
Block a user