improve nf7::gui::FileFactory

This commit is contained in:
falsycat 2022-08-14 22:50:03 +09:00
parent 540ce3bb0c
commit b1fc1a2a7d
10 changed files with 146 additions and 156 deletions

View File

@ -64,6 +64,7 @@ target_sources(nf7
common/gui_dnd.hh
common/gui_context.hh
common/gui_file.hh
common/gui_file.cc
common/gui_node.hh
common/gui_popup.hh
common/gui_resizer.hh

View File

@ -12,9 +12,8 @@ using namespace std::literals;
namespace nf7 {
FileHolder::FileHolder(
nf7::File& owner, std::string_view id, std::string_view iface, const FileHolder* src) :
owner_(&owner), id_(id), popup_config_(*this, iface) {
FileHolder::FileHolder(nf7::File& owner, std::string_view id, const FileHolder* src) :
owner_(&owner), id_(id), popup_config_(*this) {
if (src) {
if (src->own()) {
entity_ = src->file()->Clone(owner.env());
@ -170,7 +169,7 @@ void FileHolder::ConfigPopup::Update() noexcept {
if (ImGui::RadioButton("ref", type_ == 1)) { type_ = 1; }
if (type_ == 0) {
if (factory_.Update(owner)) {
if (factory_.Update()) {
// TODO
}

View File

@ -37,8 +37,7 @@ class FileHolder : public nf7::FileBase::Feature {
using Entity = std::variant<
std::monostate, nf7::File::Path, std::shared_ptr<nf7::File>>;
FileHolder(nf7::File& owner, std::string_view id, std::string_view iface,
const FileHolder* src = nullptr);
FileHolder(nf7::File& owner, std::string_view id, const FileHolder* src = nullptr);
FileHolder(const FileHolder&) = delete;
FileHolder(FileHolder&&) = delete;
FileHolder& operator=(const FileHolder&) = delete;
@ -116,8 +115,10 @@ class FileHolder : public nf7::FileBase::Feature {
// GUI popup
struct ConfigPopup final : nf7::gui::Popup {
public:
ConfigPopup(FileHolder& h, std::string_view iface) noexcept :
nf7::gui::Popup("ConfigPopup"), h_(&h), factory_({std::string {iface}}) {
ConfigPopup(FileHolder& h) noexcept :
nf7::gui::Popup("ConfigPopup"),
h_(&h),
factory_(*h.owner_, [](auto&) { return true; }) {
}
void Open() noexcept;
@ -126,9 +127,9 @@ class FileHolder : public nf7::FileBase::Feature {
private:
FileHolder* const h_;
uint32_t type_;
std::string path_;
nf7::gui::FileFactory<0> factory_;
uint32_t type_;
std::string path_;
nf7::gui::FileFactory factory_;
} popup_config_;

94
common/gui_file.cc Normal file
View File

@ -0,0 +1,94 @@
#include "common/gui_file.hh"
#include <imgui_stdlib.h>
namespace nf7::gui {
bool FileFactory::Update() noexcept {
const auto em = ImGui::GetFontSize();
ImGui::PushItemWidth(16*em);
if (ImGui::IsWindowAppearing()) {
name_ = "new_file";
type_filter_ = "";
}
if (flags_ & kNameInput) {
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
ImGui::InputText("name", &name_);
ImGui::Spacing();
}
if (ImGui::BeginListBox("type", {16*em, 8*em})) {
for (const auto& reg : nf7::File::registry()) {
const auto& t = *reg.second;
const bool name_match =
type_filter_.empty() || t.name().find(type_filter_) != std::string::npos;
const bool sel = (type_ == &t);
if (!name_match || !filter_(t)) {
if (sel) type_ = nullptr;
continue;
}
constexpr auto kSelectableFlags =
ImGuiSelectableFlags_SpanAllColumns |
ImGuiSelectableFlags_AllowItemOverlap;
if (ImGui::Selectable(t.name().c_str(), sel, kSelectableFlags)) {
type_ = &t;
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
t.UpdateTooltip();
ImGui::EndTooltip();
}
}
ImGui::EndListBox();
}
ImGui::InputTextWithHint("##type_filter", "search...", &type_filter_);
ImGui::PopItemWidth();
ImGui::Spacing();
// input validation
bool err = false;
if (type_ == nullptr) {
ImGui::Bullet(); ImGui::TextUnformatted("type is not selected");
err = true;
}
if (flags_ & kNameInput) {
try {
nf7::File::Path::ValidateTerm(name_);
} catch (Exception& e) {
ImGui::Bullet(); ImGui::Text("invalid name: %s", e.msg().c_str());
err = true;
}
if (flags_ & kNameDupCheck) {
if (owner_->Find(name_)) {
ImGui::Bullet(); ImGui::Text("name duplicated");
err = true;
}
}
}
bool ret = false;
if (!err) {
if (ImGui::Button("ok")) {
ImGui::CloseCurrentPopup();
ret = true;
}
if (ImGui::IsItemHovered()) {
const auto path = owner_->abspath().Stringify();
if (flags_ & kNameInput) {
ImGui::SetTooltip(
"create %s as '%s' on '%s'", type_->name().c_str(), name_.c_str(), path.c_str());
} else {
ImGui::SetTooltip("create %s on '%s'", type_->name().c_str(), path.c_str());
}
}
}
return ret;
}
} // namespace nf7::gui

View File

@ -1,155 +1,47 @@
#pragma once
#include <algorithm>
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include <imgui.h>
#include <imgui_stdlib.h>
#include "nf7.hh"
#include "common/gui_dnd.hh"
namespace nf7::gui {
enum FileFactoryFlag : uint8_t {
kNameInput = 1 << 0,
kNameDupCheck = 1 << 1,
};
template <uint8_t kFlags>
struct FileFactory final {
class FileFactory final {
public:
FileFactory(std::vector<std::string>&& type_flags_and,
std::vector<std::string>&& type_flags_or = {},
std::string_view default_name = "new_file") noexcept :
type_flags_and_(std::move(type_flags_and)),
type_flags_or_(std::move(type_flags_or)),
default_name_(default_name) {
enum Flag : uint8_t {
kNameInput = 1 << 0,
kNameDupCheck = 1 << 1,
};
using Flags = uint8_t;
using Filter = std::function<bool(const nf7::File::TypeInfo&)>;
FileFactory(nf7::File& owner, Filter&& filter, Flags flags = 0) noexcept :
owner_(&owner), filter_(std::move(filter)), flags_(flags) {
}
bool Update(nf7::File& owner) noexcept {
const auto em = ImGui::GetFontSize();
ImGui::PushItemWidth(16*em);
if (ImGui::IsWindowAppearing()) {
name_ = default_name_;
type_filter_ = "";
}
if constexpr (kFlags & FileFactoryFlag::kNameInput) {
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
ImGui::InputText("name", &name_);
ImGui::Spacing();
}
if (ImGui::BeginListBox("type", {16*em, 8*em})) {
for (const auto& reg : nf7::File::registry()) {
const auto& t = *reg.second;
const auto flag_matcher =
[&t](const auto& flag) { return t.flags().contains(flag); };
const bool flag_and_match = std::all_of(
type_flags_and_.begin(), type_flags_and_.end(), flag_matcher);
const bool flag_or_match =
type_flags_or_.empty() ||
std::any_of(type_flags_or_.begin(), type_flags_or_.end(), flag_matcher);
if (!flag_and_match || !flag_or_match) continue;
const bool name_match =
type_filter_.empty() || t.name().find(type_filter_) != std::string::npos;
const bool sel = (type_ == &t);
if (!name_match) {
if (sel) type_ = nullptr;
continue;
}
constexpr auto kSelectableFlags =
ImGuiSelectableFlags_SpanAllColumns |
ImGuiSelectableFlags_AllowItemOverlap;
if (ImGui::Selectable(t.name().c_str(), sel, kSelectableFlags)) {
type_ = &t;
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
t.UpdateTooltip();
ImGui::EndTooltip();
}
}
ImGui::EndListBox();
}
ImGui::InputTextWithHint("##type_filter", "search...", &type_filter_);
ImGui::PopItemWidth();
ImGui::Spacing();
// input validation
bool err = false;
if (type_ == nullptr) {
ImGui::Bullet(); ImGui::TextUnformatted("type is not selected");
err = true;
}
if constexpr (kFlags & FileFactoryFlag::kNameInput) {
try {
nf7::File::Path::ValidateTerm(name_);
} catch (Exception& e) {
ImGui::Bullet(); ImGui::Text("invalid name: %s", e.msg().c_str());
err = true;
}
if constexpr ((kFlags & FileFactoryFlag::kNameDupCheck) != 0) {
if (owner.Find(name_)) {
ImGui::Bullet(); ImGui::Text("name duplicated");
err = true;
}
}
}
bool ret = false;
if (!err) {
if (ImGui::Button("ok")) {
ImGui::CloseCurrentPopup();
ret = true;
}
if (ImGui::IsItemHovered()) {
const auto path = owner.abspath().Stringify();
if constexpr (kFlags & FileFactoryFlag::kNameInput) {
ImGui::SetTooltip(
"create %s as '%s' on '%s'", type_->name().c_str(), name_.c_str(), path.c_str());
} else {
ImGui::SetTooltip("create %s on '%s'", type_->name().c_str(), path.c_str());
}
}
}
return ret;
bool Update() noexcept;
std::unique_ptr<nf7::File> Create(nf7::Env& env) noexcept {
return type_? type_->Create(env): nullptr;
}
const std::string& name() const noexcept { return name_; }
const nf7::File::TypeInfo& type() const noexcept { return *type_; }
private:
std::vector<std::string> type_flags_and_;
std::vector<std::string> type_flags_or_;
std::string default_name_;
nf7::File* const owner_;
const Filter filter_;
const Flags flags_;
std::string name_;
const nf7::File::TypeInfo* type_ = nullptr;
std::string type_filter_;
};
inline bool InputFilePath(const char* id, std::string* path) noexcept {
bool ret = ImGui::InputText(id, path, ImGuiInputTextFlags_EnterReturnsTrue);
if (ImGui::BeginDragDropTarget()) {
if (auto str = gui::dnd::Accept<std::string>(gui::dnd::kFilePath)) {
*path = *str;
ret = true;
}
ImGui::EndDragDropTarget();
}
return ret;
}
} // namespace nf7::gui

View File

@ -6,7 +6,7 @@
namespace nf7::gui {
struct Popup {
class Popup {
public:
Popup(const char* name, ImGuiWindowFlags flags = 0) noexcept :
name_(name), flags_(flags) {

View File

@ -86,6 +86,7 @@ class Network final : public nf7::File,
ItemList&& items = {},
NodeLinkStore&& links = {}) noexcept :
File(kType, env), DirItem(DirItem::kMenu | DirItem::kTooltip),
factory_(*this, [](auto& t) { return t.flags().contains("Node"); }),
win_(*this, "Editor Node/Network", win),
items_(std::move(items)), links_(std::move(links)) {
Initialize();
@ -141,6 +142,8 @@ class Network final : public nf7::File,
const char* popup_ = nullptr;
ImVec2 canvas_action_pos_;
nf7::gui::FileFactory factory_;
// persistent params
gui::Window win_;
std::vector<std::unique_ptr<Item>> items_;
@ -1096,11 +1099,9 @@ void Network::Update() noexcept {
// node add popup
if (ImGui::BeginPopup("AddPopup")) {
static nf7::gui::FileFactory<0> p({"File_Factory",}, {"Node"});
ImGui::TextUnformatted("Node/Network: adding new Node...");
if (p.Update(*this)) {
auto item = std::make_unique<Item>(next_++, p.type().Create(env()));
if (factory_.Update()) {
auto item = std::make_unique<Item>(next_++, factory_.Create(env()));
auto ctx = std::make_shared<nf7::GenericContext>(*this, "adding new node");
auto& item_ref = *item;

View File

@ -45,7 +45,7 @@ class Call final : public nf7::FileBase, public nf7::Sequencer {
Sequencer::kTooltip |
Sequencer::kParamPanel),
life_(*this),
callee_(*this, "callee", "Node", callee),
callee_(*this, "callee", callee),
mem_(*this, Data {*this, expects}){
callee_.onChange = [this]() {
mem_.Commit();

View File

@ -131,7 +131,9 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
struct AddItemPopup final : nf7::gui::Popup {
public:
AddItemPopup(TL& f) noexcept :
Popup("AddItemPopup"), owner_(&f), factory_({"Sequencer"}) {
Popup("AddItemPopup"),
owner_(&f),
factory_(f, [](auto& t) { return t.flags().contains("Sequencer"); }) {
}
void Open(uint64_t t, TL::Layer& l) noexcept {
@ -147,7 +149,7 @@ class TL final : public nf7::File, public nf7::DirItem, public nf7::Node {
uint64_t target_time_ = 0;
TL::Layer* target_layer_ = nullptr;
nf7::gui::FileFactory<0> factory_;
nf7::gui::FileFactory factory_;
} popup_add_item_;
struct ConfigPopup final : nf7::gui::Popup {
public:
@ -1595,7 +1597,7 @@ void TL::Item::Update() noexcept {
void TL::AddItemPopup::Update() noexcept {
if (Popup::Begin()) {
ImGui::TextUnformatted("Sequencer/Timeline: adding new item...");
if (factory_.Update(*owner_)) {
if (factory_.Update()) {
ImGui::CloseCurrentPopup();
auto& layer = *target_layer_;

View File

@ -39,10 +39,13 @@ class Dir final : public File,
Dir(Env& env, ItemMap&& items = {}, const gui::Window* src = nullptr) noexcept :
File(kType, env),
DirItem(DirItem::kTree |
DirItem::kMenu |
DirItem::kTooltip |
DirItem::kDragDropTarget),
DirItem(nf7::DirItem::kTree |
nf7::DirItem::kMenu |
nf7::DirItem::kTooltip |
nf7::DirItem::kDragDropTarget),
factory_(*this, [](auto& t) { return t.flags().contains("DirItem"); },
nf7::gui::FileFactory::kNameInput |
nf7::gui::FileFactory::kNameDupCheck),
items_(std::move(items)), win_(*this, "TreeView System/Dir", src) {
}
@ -118,6 +121,8 @@ class Dir final : public File,
std::string rename_target_;
nf7::gui::FileFactory factory_;
// persistent params
ItemMap items_;
gui::Window win_;
@ -150,15 +155,10 @@ void Dir::Update() noexcept {
// new item popup
if (ImGui::BeginPopup("NewItemPopup")) {
static nf7::gui::FileFactory<
nf7::gui::kNameInput | nf7::gui::kNameDupCheck> p(
{"File_Factory", "DirItem"});
ImGui::TextUnformatted("System/Dir: adding new file...");
if (p.Update(*this)) {
if (factory_.Update()) {
auto ctx = std::make_shared<nf7::GenericContext>(*this, "adding new item");
auto task = [this, name = p.name(), &type = p.type()]() {
Add(name, type.Create(env()));
};
auto task = [this]() { Add(factory_.name(), factory_.Create(env())); };
env().ExecMain(ctx, std::move(task));
}
ImGui::EndPopup();