improve nf7::gui::FileFactory
This commit is contained in:
parent
540ce3bb0c
commit
b1fc1a2a7d
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
94
common/gui_file.cc
Normal 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
|
@ -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
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
namespace nf7::gui {
|
||||
|
||||
struct Popup {
|
||||
class Popup {
|
||||
public:
|
||||
Popup(const char* name, ImGuiWindowFlags flags = 0) noexcept :
|
||||
name_(name), flags_(flags) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user