move an implementation of System/Dir's NewFilePopup to common utility

This commit is contained in:
falsycat 2022-05-30 13:29:45 +09:00
parent 27469b640e
commit 3dfad9768b
3 changed files with 144 additions and 76 deletions

View File

@ -49,6 +49,7 @@ target_sources(nf7
common/file_ref.hh
common/generic_context.hh
common/generic_type_info.hh
common/gui_file.hh
common/gui_window.hh
common/logger.hh
common/logger_pool.hh

131
common/gui_file.hh Normal file
View File

@ -0,0 +1,131 @@
#pragma once
#include <algorithm>
#include <string>
#include <string_view>
#include <vector>
#include "nf7.hh"
namespace nf7::gui {
enum FileCreatePopupFlag : uint8_t {
kNameInput = 1 << 0,
kNameDupCheck = 1 << 1,
};
template <uint8_t kFlags>
struct FileCreatePopup final {
public:
FileCreatePopup(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) {
}
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 & FileCreatePopupFlag::kNameInput) {
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
ImGui::InputText("name", &name_);
ImGui::Spacing();
}
if (ImGui::BeginListBox("type", {16*em, 4*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;
}
}
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 & FileCreatePopupFlag::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 & FileCreatePopupFlag::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 constexpr (kFlags & FileCreatePopupFlag::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;
}
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_;
std::string name_;
const nf7::File::TypeInfo* type_;
std::string type_filter_;
};
} // namespace nf7

View File

@ -15,6 +15,7 @@
#include "common/dir_item.hh"
#include "common/generic_context.hh"
#include "common/generic_type_info.hh"
#include "common/gui_file.hh"
#include "common/gui_window.hh"
#include "common/ptr_selector.hh"
#include "common/yas.hh"
@ -136,83 +137,18 @@ void Dir::Update() noexcept {
// new item popup
if (ImGui::BeginPopup("NewItemPopup")) {
static const TypeInfo* selecting = nullptr;
static std::string filter = "";
static std::string name = "";
static nf7::gui::FileCreatePopup<
nf7::gui::kNameInput | nf7::gui::kNameDupCheck> p(
{"File_Factory", "DirItem"});
ImGui::TextUnformatted("System/Dir: adding new file...");
if (p.Update(*this)) {
auto ctx = std::make_shared<GenericContext>(env(), id());
ctx->description() = "adding new file on "+abspath().Stringify();
ImGui::PushItemWidth(16*em);
ImGui::TextUnformatted("System/Dir: adding new item...");
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
ImGui::InputText("name", &name);
ImGui::Spacing();
ImGui::InputTextWithHint("type", "search", &filter);
if (ImGui::BeginListBox("##type_list", {16*em, 4*em})) {
for (const auto& reg : registry()) {
const auto& t = *reg.second;
if (!t.flags().contains("DirItem")) continue;
if (!t.flags().contains("File_Factory")) continue;
const bool name_match =
filter.empty() || t.name().find(filter) != std::string::npos;
const bool sel = (selecting == &t);
if (!name_match) {
if (sel) selecting = nullptr;
continue;
}
ImGui::PushID(&t);
constexpr auto kFlags =
ImGuiSelectableFlags_SpanAllColumns |
ImGuiSelectableFlags_AllowItemOverlap;
if (ImGui::Selectable("##selectable", sel, kFlags)) {
selecting = &t;
}
ImGui::SameLine();
ImGui::TextUnformatted(t.name().c_str());
ImGui::PopID();
}
ImGui::EndListBox();
}
ImGui::PopItemWidth();
ImGui::Spacing();
// input validation
bool err = false;
if (selecting == nullptr) {
ImGui::Bullet(); ImGui::TextUnformatted("type is not selected");
err = true;
}
try {
Path::ValidateTerm(name);
} catch (Exception& e) {
ImGui::Bullet(); ImGui::Text("invalid name: %s", e.msg().c_str());
err = true;
}
if (items_.find(name) != items_.end()) {
ImGui::Bullet(); ImGui::Text("name duplicated");
err = true;
}
if (!err) {
if (ImGui::Button("ok")) {
ImGui::CloseCurrentPopup();
auto ctx = std::make_shared<GenericContext>(env(), id());
ctx->description() = "adding new file on "+abspath().Stringify();
auto task = [this, name = std::move(name), type = selecting]() {
Add(name, type->Create(env()));
};
env().ExecMain(ctx, std::move(task));
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"create %s as '%s' on '%s'",
selecting->name().c_str(), name.c_str(), abspath().Stringify().c_str());
}
auto task = [this, name = p.name(), &type = p.type()]() {
Add(name, type.Create(env()));
};
env().ExecMain(ctx, std::move(task));
}
ImGui::EndPopup();
}