From cfc55968851881c54e17f1ca7608bc949c589d28 Mon Sep 17 00:00:00 2001 From: falsycat Date: Fri, 19 Aug 2022 23:12:15 +0900 Subject: [PATCH] improve nf7::gui::Value --- CMakeLists.txt | 2 + common/gui_value.cc | 115 +++++++++++++++++++++++++ common/gui_value.hh | 139 +++++------------------------- file/node_imm.cc | 176 ++++++-------------------------------- file/sequencer_adaptor.cc | 2 + 5 files changed, 167 insertions(+), 267 deletions(-) create mode 100644 common/gui_value.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 46fb5ab..be7a87a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,8 @@ target_sources(nf7 common/gui_resizer.hh common/gui_timeline.hh common/gui_timeline.cc + common/gui_value.hh + common/gui_value.cc common/gui_window.hh common/history.hh common/life.hh diff --git a/common/gui_value.cc b/common/gui_value.cc new file mode 100644 index 0000000..a9f6842 --- /dev/null +++ b/common/gui_value.cc @@ -0,0 +1,115 @@ +#include "common/gui_value.hh" + + +namespace nf7::gui { + +bool Value::ReplaceType(Type t) noexcept { + if (type_ == t) return false; + + type_ = t; + switch (type_) { + case nf7::gui::Value::kPulse: + entity_ = nf7::Value::Pulse {}; + break; + case nf7::gui::Value::kInteger: + entity_ = nf7::Value::Integer {0}; + break; + case nf7::gui::Value::kScalar: + case nf7::gui::Value::kNormalizedScalar: + entity_ = nf7::Value::Scalar {0}; + break; + case nf7::gui::Value::kString: + case nf7::gui::Value::kMultilineString: + entity_ = nf7::Value::String {}; + break; + default: + assert(false); + } + return true; +} + +void Value::ValidateValue() const { + bool valid = true; + switch (type_) { + case nf7::gui::Value::kPulse: + valid = entity_.isPulse(); + break; + case nf7::gui::Value::kInteger: + valid = entity_.isInteger(); + break; + case nf7::gui::Value::kScalar: + case nf7::gui::Value::kNormalizedScalar: + valid = entity_.isScalar(); + break; + case nf7::gui::Value::kString: + case nf7::gui::Value::kMultilineString: + valid = entity_.isString(); + break; + } + if (!valid) { + throw nf7::DeserializeException {"invalid entity type"}; + } +} + +bool Value::UpdateTypeButton(const char* name, bool small) noexcept { + if (name == nullptr) { + name = StringifyShortType(type_); + } + + if (small) { + ImGui::SmallButton(name); + } else { + ImGui::Button(name); + } + + bool ret = false; + if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonLeft)) { + for (const auto t : kTypes) { + if (ImGui::MenuItem(StringifyType(t), nullptr, type_ == t)) { + ret |= ReplaceType(t); + } + } + ImGui::EndPopup(); + } + return ret; +} + +bool Value::UpdateEditor() noexcept { + bool ret = false; + const auto w = ImGui::CalcItemWidth(); + + const auto em = ImGui::GetFontSize(); + switch (type_) { + case kPulse: + ImGui::BeginDisabled(); + ImGui::Button("PULSE", {w, 0}); + ImGui::EndDisabled(); + break; + case kInteger: + ImGui::DragScalar("##value", ImGuiDataType_S64, &entity_.integer()); + ret |= ImGui::IsItemDeactivatedAfterEdit(); + break; + case kScalar: + ImGui::DragScalar("##value", ImGuiDataType_Double, &entity_.scalar()); + ret |= ImGui::IsItemDeactivatedAfterEdit(); + break; + case kNormalizedScalar: + ImGui::DragScalar("##value", ImGuiDataType_Double, &entity_.scalar()); + ret |= ImGui::IsItemDeactivatedAfterEdit(); + break; + case kString: + ImGui::InputTextWithHint("##value", "string", &entity_.string()); + ret |= ImGui::IsItemDeactivatedAfterEdit(); + break; + case kMultilineString: + ImGui::InputTextMultiline("##value", &entity_.string(), {w, 2.4f*em}); + ret |= ImGui::IsItemDeactivatedAfterEdit(); + break; + default: + assert(false); + } + + return ret; +} + +} // namespace nf7::gui diff --git a/common/gui_value.hh b/common/gui_value.hh index 9e1fa86..f32bb0b 100644 --- a/common/gui_value.hh +++ b/common/gui_value.hh @@ -20,12 +20,10 @@ namespace nf7::gui { class Value { public: enum Type { - kPulse, - kInteger, - kScalar, - kNormalizedScalar, - kString, - kMultilineString, + kPulse, kInteger, kScalar, kNormalizedScalar, kString, kMultilineString, + }; + static inline const Type kTypes[] = { + kPulse, kInteger, kScalar, kNormalizedScalar, kString, kMultilineString, }; static const char* StringifyType(Type t) noexcept { @@ -40,6 +38,18 @@ class Value { assert(false); return nullptr; } + static const char* StringifyShortType(Type t) noexcept { + switch (t) { + case kPulse: return "Pulse"; + case kInteger: return "Integer"; + case kScalar: return "Scalar"; + case kNormalizedScalar: return "NScalar"; + case kString: return "String"; + case kMultilineString: return "MString"; + } + assert(false); + return nullptr; + } static Type ParseType(std::string_view v) { return v == "Pulse"? kPulse: @@ -57,125 +67,20 @@ class Value { Value& operator=(const Value&) = default; Value& operator=(Value&&) = default; - bool ReplaceType(Type t) noexcept { - if (type_ == t) return false; + bool ReplaceType(Type t) noexcept; - type_ = t; - switch (type_) { - case nf7::gui::Value::kPulse: - entity_ = nf7::Value::Pulse {}; - break; - case nf7::gui::Value::kInteger: - entity_ = nf7::Value::Integer {0}; - break; - case nf7::gui::Value::kScalar: - case nf7::gui::Value::kNormalizedScalar: - entity_ = nf7::Value::Scalar {0}; - break; - case nf7::gui::Value::kString: - case nf7::gui::Value::kMultilineString: - entity_ = nf7::Value::String {}; - break; - default: - assert(false); - } - return true; - } - - void ReplaceEntity(const nf7::Value& v) noexcept { + void ReplaceEntity(const nf7::Value& v) { entity_ = v; ValidateValue(); } - void ReplaceEntity(nf7::Value&& v) noexcept { + void ReplaceEntity(nf7::Value&& v) { entity_ = std::move(v); ValidateValue(); } - void ValidateValue() const { - bool valid = true; - switch (type_) { - case nf7::gui::Value::kPulse: - valid = entity_.isPulse(); - break; - case nf7::gui::Value::kInteger: - valid = entity_.isInteger(); - break; - case nf7::gui::Value::kScalar: - case nf7::gui::Value::kNormalizedScalar: - valid = entity_.isScalar(); - break; - case nf7::gui::Value::kString: - case nf7::gui::Value::kMultilineString: - valid = entity_.isString(); - break; - } - if (!valid) { - throw nf7::DeserializeException {"invalid entity type"}; - } - } + void ValidateValue() const; - bool UpdateEditor() noexcept { - bool ret = false; - const auto w = ImGui::CalcItemWidth(); - - ImGui::Button("T"); - if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonLeft)) { - if (ImGui::MenuItem("Pulse", nullptr, type_ == kPulse)) { - ret |= ReplaceType(kPulse); - } - if (ImGui::MenuItem("Integer", nullptr, type_ == kInteger)) { - ret |= ReplaceType(kInteger); - } - if (ImGui::MenuItem("Scalar", nullptr, type_ == kScalar)) { - ret |= ReplaceType(kScalar); - } - if (ImGui::MenuItem("Normalized Scalar", nullptr, type_ == kNormalizedScalar)) { - ret |= ReplaceType(kNormalizedScalar); - } - if (ImGui::MenuItem("String", nullptr, type_ == kString)) { - ret |= ReplaceType(kString); - } - if (ImGui::MenuItem("Multiline String", nullptr, type_ == kMultilineString)) { - ret |= ReplaceType(kMultilineString); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - - const auto em = ImGui::GetFontSize(); - ImGui::PushItemWidth(w-ImGui::GetItemRectSize().x); - switch (type_) { - case kPulse: - ImGui::BeginDisabled(); - ImGui::Button("PULSE", {w, 0}); - ImGui::EndDisabled(); - break; - case kInteger: - ImGui::DragScalar("##value", ImGuiDataType_S64, &entity_.integer()); - ret |= ImGui::IsItemDeactivatedAfterEdit(); - break; - case kScalar: - ImGui::DragScalar("##value", ImGuiDataType_Double, &entity_.scalar()); - ret |= ImGui::IsItemDeactivatedAfterEdit(); - break; - case kNormalizedScalar: - ImGui::DragScalar("##value", ImGuiDataType_Double, &entity_.scalar()); - ret |= ImGui::IsItemDeactivatedAfterEdit(); - break; - case kString: - ImGui::InputTextWithHint("##value", "string", &entity_.string()); - ret |= ImGui::IsItemDeactivatedAfterEdit(); - break; - case kMultilineString: - ImGui::InputTextMultiline("##value", &entity_.string(), {w, 2.4f*em}); - ret |= ImGui::IsItemDeactivatedAfterEdit(); - break; - default: - assert(false); - } - ImGui::PopItemWidth(); - - return ret; - } + bool UpdateTypeButton(const char* name = nullptr, bool small = false) noexcept; + bool UpdateEditor() noexcept; Type type() const noexcept { return type_; } const nf7::Value& entity() const noexcept { return entity_; } diff --git a/file/node_imm.cc b/file/node_imm.cc index 3e41035..715edda 100644 --- a/file/node_imm.cc +++ b/file/node_imm.cc @@ -21,6 +21,7 @@ #include "common/generic_type_info.hh" #include "common/gui_node.hh" #include "common/gui_resizer.hh" +#include "common/gui_value.hh" #include "common/life.hh" #include "common/node.hh" #include "common/ptr_selector.hh" @@ -45,44 +46,26 @@ class Imm final : public nf7::File, public nf7::DirItem, public nf7::Node { class Lambda; - enum Type { kPulse, kInteger, kScalar, kScalarNormal, kStringText, }; - static inline const std::vector> kTypeNames = { - {kPulse, "pulse"}, - {kInteger, "integer"}, - {kScalar, "scalar"}, - {kScalarNormal, "scalar/normal"}, - {kStringText, "string/text"}, - }; - - Imm(Env& env, Type type = kInteger, nf7::Value&& v = nf7::Value::Integer {0}) noexcept : + Imm(Env& env, nf7::gui::Value&& v = {}) noexcept : nf7::File(kType, env), nf7::DirItem(DirItem::kNone), - life_(*this), mem_(*this, {type, std::move(v)}) { + life_(*this), mem_(*this, std::move(v)) { input_ = {"in"}; 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(); + ar(mem_.data()); } void Serialize(Serializer& ar) const noexcept override { - const auto& data = mem_.data(); - ar(std::string_view{StringifyType(data.type)}, data.value, data.size); + ar(mem_.data()); } std::unique_ptr Clone(Env& env) const noexcept override { - const auto& data = mem_.data(); - return std::make_unique(env, data.type, nf7::Value{data.value}); + return std::make_unique(env, nf7::gui::Value {mem_.data()}); } std::shared_ptr CreateLambda(const std::shared_ptr&) noexcept override; void UpdateNode(Node::Editor&) noexcept override; - void UpdateEditor(Node::Editor&, float w) noexcept; File::Interface* interface(const std::type_info& t) noexcept override { return InterfaceSelector< @@ -92,33 +75,7 @@ class Imm final : public nf7::File, public nf7::DirItem, public nf7::Node { private: nf7::Life life_; - 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 mem_; - - - 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; - } + nf7::GenericMemento mem_; }; class Imm::Lambda final : public Node::Lambda, @@ -132,7 +89,7 @@ class Imm::Lambda final : public Node::Lambda, const std::shared_ptr& caller) noexcept override { if (!f_) return; if (name == "in") { - caller->Handle("out", f_->mem_.data().value, shared_from_this()); + caller->Handle("out", f_->mem_.data().entity(), shared_from_this()); return; } } @@ -146,110 +103,29 @@ std::shared_ptr Imm::CreateLambda( } -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::BeginInputSlot("in", 1)) { - gui::NodeSocket(); - ImNodes::EndSlot(); - } - ImGui::SameLine(); - UpdateEditor(editor, right-left); - ImGui::SameLine(); - if (ImNodes::BeginOutputSlot("out", 1)) { - gui::NodeSocket(); - ImNodes::EndSlot(); - } - ImGui::PopItemWidth(); -} -void Imm::UpdateEditor(Node::Editor&, float w) noexcept { - static const double kZero = 0., kOne = 1.; - +void Imm::UpdateNode(Node::Editor&) noexcept { const auto em = ImGui::GetFontSize(); - bool mod = false, com = false; + ImGui::TextUnformatted("Node/Imm"); + ImGui::SameLine(); + mem_.data().UpdateTypeButton(nullptr, true); - 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 (ImNodes::BeginInputSlot("in", 1)) { + ImGui::AlignTextToFramePadding(); + nf7::gui::NodeSocket(); + ImNodes::EndSlot(); } + ImGui::SameLine(); - 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; + ImGui::PushItemWidth(8*em); + mem_.data().UpdateEditor(); + ImGui::PopItemWidth(); + + ImGui::SameLine(); + if (ImNodes::BeginOutputSlot("out", 1)) { + ImGui::AlignTextToFramePadding(); + nf7::gui::NodeSocket(); + ImNodes::EndSlot(); } } diff --git a/file/sequencer_adaptor.cc b/file/sequencer_adaptor.cc index 1436db3..f797d4e 100644 --- a/file/sequencer_adaptor.cc +++ b/file/sequencer_adaptor.cc @@ -266,6 +266,8 @@ void Adaptor::UpdateParamPanel(Sequencer::Editor&) noexcept { ImGui::PushID(static_cast(i)); if (ImGui::TableNextColumn()) { + commit |= p.second.UpdateTypeButton("T"); + ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); commit |= p.second.UpdateEditor(); }