improve nf7::gui::Value
This commit is contained in:
parent
dbbaac5c8e
commit
cfc5596885
@ -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
|
||||
|
115
common/gui_value.cc
Normal file
115
common/gui_value.cc
Normal file
@ -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
|
@ -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_; }
|
||||
|
176
file/node_imm.cc
176
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<std::pair<Type, const char*>> 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<File> Clone(Env& env) const noexcept override {
|
||||
const auto& data = mem_.data();
|
||||
return std::make_unique<Imm>(env, data.type, nf7::Value{data.value});
|
||||
return std::make_unique<Imm>(env, nf7::gui::Value {mem_.data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<Node::Lambda> CreateLambda(const std::shared_ptr<Node::Lambda>&) 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<Imm> 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<Data> 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<nf7::gui::Value> mem_;
|
||||
};
|
||||
|
||||
class Imm::Lambda final : public Node::Lambda,
|
||||
@ -132,7 +89,7 @@ class Imm::Lambda final : public Node::Lambda,
|
||||
const std::shared_ptr<Node::Lambda>& 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<Node::Lambda> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,6 +266,8 @@ void Adaptor::UpdateParamPanel(Sequencer::Editor&) noexcept {
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
commit |= p.second.UpdateTypeButton("T");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
commit |= p.second.UpdateEditor();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user