separate core logic into nf7::GenericDir from an implementation of System/Dir
This commit is contained in:
parent
1f5f46c925
commit
5d79d7631b
@ -76,6 +76,7 @@ target_sources(nf7
|
||||
common/future.hh
|
||||
common/generic_config.hh
|
||||
common/generic_context.hh
|
||||
common/generic_dir.hh
|
||||
common/generic_history.hh
|
||||
common/generic_memento.hh
|
||||
common/generic_type_info.hh
|
||||
|
122
common/generic_dir.hh
Normal file
122
common/generic_dir.hh
Normal file
@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/map.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/dir.hh"
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/yas_nf7.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class GenericDir : public nf7::FileBase::Feature, public nf7::Dir {
|
||||
public:
|
||||
using ItemMap = std::map<std::string, std::unique_ptr<nf7::File>>;
|
||||
|
||||
GenericDir() = delete;
|
||||
GenericDir(nf7::FileBase& f, ItemMap&& items = {}) noexcept :
|
||||
nf7::FileBase::Feature(f), f_(f), items_(std::move(items)) {
|
||||
}
|
||||
GenericDir(const GenericDir&) = delete;
|
||||
GenericDir(GenericDir&&) = delete;
|
||||
GenericDir& operator=(const GenericDir&) = delete;
|
||||
GenericDir& operator=(GenericDir&&) = delete;
|
||||
|
||||
void serialize(auto& ar) { ar(items_); }
|
||||
|
||||
ItemMap CloneItems(nf7::Env& env) const {
|
||||
ItemMap ret;
|
||||
for (auto& p : items_) {
|
||||
ret[p.first] = p.second->Clone(env);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
std::string GetUniqueName(std::string_view name) const noexcept {
|
||||
auto ret = std::string {name};
|
||||
while (items_.end() != items_.find(ret)) {
|
||||
ret += "_dup";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
nf7::File* Find(std::string_view name) const noexcept override {
|
||||
auto itr = items_.find(std::string {name});
|
||||
return itr != items_.end()? itr->second.get(): nullptr;
|
||||
}
|
||||
nf7::File& Add(std::string_view name, std::unique_ptr<File>&& f) override {
|
||||
const auto sname = std::string(name);
|
||||
|
||||
auto [itr, ok] = items_.emplace(sname, std::move(f));
|
||||
if (!ok) throw nf7::Dir::DuplicateException {"item name duplication: "+sname};
|
||||
|
||||
auto& ret = *itr->second;
|
||||
if (f_.id()) ret.MoveUnder(f_, name);
|
||||
return ret;
|
||||
}
|
||||
std::unique_ptr<nf7::File> Remove(std::string_view name) noexcept override {
|
||||
auto itr = items_.find(std::string(name));
|
||||
if (itr == items_.end()) return nullptr;
|
||||
|
||||
auto ret = std::move(itr->second);
|
||||
items_.erase(itr);
|
||||
if (f_.id()) ret->Isolate();
|
||||
return ret;
|
||||
}
|
||||
|
||||
nf7::File* Rename(std::string_view before, std::string_view after) noexcept {
|
||||
if (auto f = Remove(before)) {
|
||||
return &Add(after, std::move(f));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
nf7::File* Renew(std::string_view name) noexcept {
|
||||
return Rename(name, name);
|
||||
}
|
||||
|
||||
const ItemMap& items() const noexcept { return items_; }
|
||||
|
||||
private:
|
||||
nf7::FileBase& f_;
|
||||
|
||||
ItemMap items_;
|
||||
|
||||
|
||||
void Update() noexcept override {
|
||||
UpdateChildren(true);
|
||||
UpdateChildren(false);
|
||||
}
|
||||
void UpdateChildren(bool early) noexcept {
|
||||
for (auto& p : items_) {
|
||||
auto& f = *p.second;
|
||||
auto* ditem = f.interface<nf7::DirItem>();
|
||||
|
||||
const bool e = ditem && (ditem->flags() & nf7::DirItem::kEarlyUpdate);
|
||||
if (e == early) f.Update();
|
||||
}
|
||||
}
|
||||
void Handle(const nf7::File::Event& e) noexcept override {
|
||||
switch (e.type) {
|
||||
case nf7::File::Event::kAdd:
|
||||
for (auto& p : items_) p.second->MoveUnder(f_, p.first);
|
||||
return;
|
||||
case nf7::File::Event::kRemove:
|
||||
for (auto& p : items_) p.second->Isolate();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nf7
|
@ -19,6 +19,7 @@
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_dir.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/gui.hh"
|
||||
#include "common/gui_dnd.hh"
|
||||
@ -31,7 +32,6 @@ namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Dir final : public nf7::FileBase,
|
||||
public nf7::Dir,
|
||||
public nf7::DirItem {
|
||||
public:
|
||||
static inline const GenericTypeInfo<Dir> kType = {"System/Dir", {"nf7::DirItem"}};
|
||||
@ -39,13 +39,13 @@ class Dir final : public nf7::FileBase,
|
||||
|
||||
using ItemMap = std::map<std::string, std::unique_ptr<File>>;
|
||||
|
||||
Dir(nf7::Env& env, ItemMap&& items = {}) noexcept :
|
||||
Dir(nf7::Env& env, nf7::GenericDir::ItemMap&& items = {}) noexcept :
|
||||
nf7::FileBase(kType, env),
|
||||
nf7::DirItem(nf7::DirItem::kTree |
|
||||
nf7::DirItem::kMenu |
|
||||
nf7::DirItem::kTooltip |
|
||||
nf7::DirItem::kDragDropTarget),
|
||||
items_(std::move(items)), win_(*this, "Tree View") {
|
||||
dir_(*this, std::move(items)), win_(*this, "Tree View") {
|
||||
win_.onConfig = []() {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSize({8*em, 8*em}, ImGuiCond_FirstUseEver);
|
||||
@ -54,68 +54,15 @@ class Dir final : public nf7::FileBase,
|
||||
}
|
||||
|
||||
Dir(nf7::Deserializer& ar) : Dir(ar.env()) {
|
||||
ar(opened_, win_);
|
||||
|
||||
uint64_t size;
|
||||
ar(size);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
std::string name;
|
||||
ar(name);
|
||||
|
||||
std::unique_ptr<nf7::File> f;
|
||||
try {
|
||||
ar(f);
|
||||
items_[name] = std::move(f);
|
||||
} catch (nf7::Exception&) {
|
||||
env().Throw(std::current_exception());
|
||||
}
|
||||
}
|
||||
ar(dir_, opened_, win_);
|
||||
}
|
||||
void Serialize(nf7::Serializer& ar) const noexcept override {
|
||||
ar(opened_, win_);
|
||||
|
||||
ar(static_cast<uint64_t>(items_.size()));
|
||||
for (auto& p : items_) {
|
||||
ar(p.first, p.second);
|
||||
}
|
||||
ar(dir_, opened_, win_);
|
||||
}
|
||||
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
|
||||
ItemMap items;
|
||||
for (const auto& item : items_) {
|
||||
items[item.first] = item.second->Clone(env);
|
||||
}
|
||||
return std::make_unique<Dir>(env, std::move(items));
|
||||
return std::make_unique<Dir>(env, dir_.CloneItems(env));
|
||||
}
|
||||
|
||||
File* PreFind(std::string_view name) const noexcept override {
|
||||
auto itr = items_.find(std::string(name));
|
||||
if (itr == items_.end()) return nullptr;
|
||||
return itr->second.get();
|
||||
}
|
||||
|
||||
File& Add(std::string_view name, std::unique_ptr<File>&& f) override {
|
||||
const auto sname = std::string(name);
|
||||
|
||||
auto [itr, ok] = items_.emplace(sname, std::move(f));
|
||||
if (!ok) throw DuplicateException("item name duplication: "+sname);
|
||||
|
||||
auto& ret = *itr->second;
|
||||
if (id()) ret.MoveUnder(*this, name);
|
||||
return ret;
|
||||
}
|
||||
std::unique_ptr<File> Remove(std::string_view name) noexcept override {
|
||||
auto itr = items_.find(std::string(name));
|
||||
if (itr == items_.end()) return nullptr;
|
||||
|
||||
auto ret = std::move(itr->second);
|
||||
items_.erase(itr);
|
||||
if (id()) ret->Isolate();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void UpdateChildren(bool early) noexcept;
|
||||
void PreUpdate() noexcept override { UpdateChildren(true); }
|
||||
void PostUpdate() noexcept override { UpdateChildren(false); }
|
||||
void UpdateTree() noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
@ -128,27 +75,20 @@ class Dir final : public nf7::FileBase,
|
||||
if (name() == "$") {
|
||||
win_.Show();
|
||||
}
|
||||
for (const auto& item : items_) item.second->MoveUnder(*this, item.first);
|
||||
return;
|
||||
case Event::kRemove:
|
||||
for (const auto& item : items_) item.second->Isolate();
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<nf7::Dir, nf7::DirItem>(t).Select(this);
|
||||
return nf7::InterfaceSelector<nf7::Dir, nf7::DirItem>(t).Select(this, &dir_);
|
||||
}
|
||||
|
||||
private:
|
||||
// persistent params
|
||||
ItemMap items_;
|
||||
gui::Window win_;
|
||||
|
||||
nf7::GenericDir dir_;
|
||||
std::unordered_set<std::string> opened_;
|
||||
gui::Window win_;
|
||||
|
||||
|
||||
static bool TestFlags(nf7::File& f, nf7::DirItem::Flags flags) noexcept
|
||||
@ -158,14 +98,6 @@ class Dir final : public nf7::FileBase,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GetUniqueName(std::string_view name) const noexcept {
|
||||
auto ret = std::string {name};
|
||||
while (Find(ret)) {
|
||||
ret += "_dup";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// imgui widgets
|
||||
void TreeView() noexcept;
|
||||
void ItemAdder() noexcept;
|
||||
@ -173,18 +105,8 @@ class Dir final : public nf7::FileBase,
|
||||
bool ValidateName(const std::string& name) noexcept;
|
||||
};
|
||||
|
||||
void Dir::UpdateChildren(bool early) noexcept {
|
||||
for (const auto& item : items_) {
|
||||
auto& f = *item.second;
|
||||
if (early == TestFlags(f, nf7::DirItem::kEarlyUpdate)) {
|
||||
ImGui::PushID(&f);
|
||||
f.Update();
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
}
|
||||
void Dir::UpdateTree() noexcept {
|
||||
for (const auto& item : items_) {
|
||||
for (const auto& item : dir_.items()) {
|
||||
const auto& name = item.first;
|
||||
auto& file = *item.second;
|
||||
ImGui::PushID(&file);
|
||||
@ -231,7 +153,7 @@ void Dir::UpdateTree() noexcept {
|
||||
if (ImGui::MenuItem("remove")) {
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "removing item"),
|
||||
[this, name]() { Remove(name); });
|
||||
[this, name]() { dir_.Remove(name); });
|
||||
}
|
||||
if (ImGui::BeginMenu("rename")) {
|
||||
ItemRenamer(name);
|
||||
@ -241,7 +163,7 @@ void Dir::UpdateTree() noexcept {
|
||||
if (ImGui::MenuItem("renew")) {
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "renewing item"),
|
||||
[this, name]() { Add(name, Remove(name)); });
|
||||
[this, name]() { dir_.Renew(name); });
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("re-initialize the item by re-adding after removing");
|
||||
@ -250,7 +172,7 @@ void Dir::UpdateTree() noexcept {
|
||||
if (ImGui::MenuItem("clone")) {
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "duplicating item"),
|
||||
[this, name, &file]() { Add(GetUniqueName(name), file.Clone(env())); });
|
||||
[this, name, &file]() { dir_.Add(dir_.GetUniqueName(name), file.Clone(env())); });
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
@ -304,14 +226,14 @@ void Dir::UpdateMenu() noexcept {
|
||||
win_.MenuItem();
|
||||
}
|
||||
void Dir::UpdateTooltip() noexcept {
|
||||
ImGui::Text("children: %zu", items_.size());
|
||||
ImGui::Text("children: %zu", dir_.items().size());
|
||||
}
|
||||
void Dir::UpdateDragDropTarget() noexcept
|
||||
try {
|
||||
nf7::File::Path p;
|
||||
if (auto pay = gui::dnd::Peek<Path>(gui::dnd::kFilePath, p)) {
|
||||
auto& target = ResolveOrThrow(p);
|
||||
if (target.parent() == this) {
|
||||
if (target.parent() == nullptr || target.parent() == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -327,13 +249,20 @@ try {
|
||||
parent = parent->parent();
|
||||
}
|
||||
|
||||
auto& dir = target.parent()->interfaceOrThrow<nf7::Dir>();
|
||||
const auto pid = target.parent()->id();
|
||||
auto& src = target.parent()->interfaceOrThrow<nf7::Dir>();
|
||||
|
||||
nf7::gui::dnd::DrawRect();
|
||||
if (pay->IsDelivery()) {
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "moving an item"),
|
||||
[this, &dir, name = target.name()]() { Add(GetUniqueName(name), dir.Remove(name)); });
|
||||
[this, pid, &src, name = target.name()]() {
|
||||
if (env().GetFile(pid)) {
|
||||
if (auto f = src.Remove(name)) {
|
||||
dir_.Add(dir_.GetUniqueName(name), std::move(f));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (nf7::File::NotImplementedException&) {
|
||||
@ -364,7 +293,7 @@ void Dir::ItemAdder() noexcept {
|
||||
static std::string name;
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
type = nullptr;
|
||||
name = GetUniqueName("new_file");
|
||||
name = dir_.GetUniqueName("new_file");
|
||||
}
|
||||
ImGui::TextUnformatted("System/Dir: adding new file...");
|
||||
|
||||
@ -418,7 +347,7 @@ void Dir::ItemAdder() noexcept {
|
||||
ImGui::CloseCurrentPopup();
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "adding new item"),
|
||||
[this]() { Add(name, type->Create(env())); });
|
||||
[this]() { dir_.Add(name, type->Create(env())); });
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,7 +377,7 @@ void Dir::ItemRenamer(const std::string& name) noexcept {
|
||||
ImGui::CloseCurrentPopup();
|
||||
env().ExecMain(
|
||||
std::make_shared<nf7::GenericContext>(*this, "renaming item"),
|
||||
[this, name]() { Add(editing_name, Remove(name)); });
|
||||
[this, name]() { dir_.Rename(name, editing_name); });
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user