improve nf7::gui::Window
This commit is contained in:
parent
dd14217f5b
commit
bb799adfb4
@ -1,72 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/utility/usertype.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/file_base.hh"
|
||||
|
||||
|
||||
namespace nf7::gui {
|
||||
|
||||
class Window {
|
||||
class Window : public nf7::FileBase::Feature {
|
||||
public:
|
||||
static std::string ConcatId(nf7::File& f, const std::string& name) noexcept {
|
||||
return f.abspath().Stringify() + " | " + std::string {name};
|
||||
}
|
||||
|
||||
Window() = delete;
|
||||
Window(File& owner, std::string_view title, const gui::Window* src = nullptr) noexcept :
|
||||
owner_(&owner), title_(title),
|
||||
shown_(src? src->shown_: false) {
|
||||
Window(nf7::FileBase& owner, std::string_view title) noexcept :
|
||||
nf7::FileBase::Feature(owner), owner_(&owner), title_(title), shown_(false) {
|
||||
}
|
||||
Window(const Window&) = delete;
|
||||
Window(Window&&) = delete;
|
||||
Window& operator=(const Window&) = delete;
|
||||
Window& operator=(Window&&) = delete;
|
||||
|
||||
bool Begin() noexcept {
|
||||
if (std::exchange(set_focus_, false)) {
|
||||
ImGui::SetNextWindowFocus();
|
||||
shown_ = true;
|
||||
}
|
||||
if (!shown_) return false;
|
||||
|
||||
need_end_ = true;
|
||||
return ImGui::Begin(id().c_str(), &shown_);
|
||||
}
|
||||
void End() noexcept {
|
||||
if (need_end_) {
|
||||
ImGui::End();
|
||||
need_end_ = false;
|
||||
}
|
||||
void serialize(auto& ar) {
|
||||
ar(shown_);
|
||||
}
|
||||
|
||||
void Show() noexcept {
|
||||
shown_ = true;
|
||||
}
|
||||
void SetFocus() noexcept {
|
||||
shown_ = true;
|
||||
set_focus_ = true;
|
||||
}
|
||||
|
||||
template <typename Ar>
|
||||
Ar& serialize(Ar& ar) {
|
||||
ar(shown_);
|
||||
return ar;
|
||||
}
|
||||
|
||||
std::string id() const noexcept {
|
||||
return ConcatId(*owner_, title_);
|
||||
}
|
||||
|
||||
bool shownInCurrentFrame() const noexcept {
|
||||
return shown_ || set_focus_;
|
||||
bool MenuItem() noexcept {
|
||||
return ImGui::MenuItem(title_.c_str(), nullptr, &shown_);
|
||||
}
|
||||
|
||||
std::string id() const noexcept { return ConcatId(*owner_, title_); }
|
||||
bool shown() const noexcept { return shown_; }
|
||||
bool& shown() noexcept { return shown_; }
|
||||
|
||||
std::function<void()> onConfig = [](){};
|
||||
std::function<void()> onUpdate;
|
||||
|
||||
private:
|
||||
File* const owner_;
|
||||
@ -77,6 +62,31 @@ class Window {
|
||||
|
||||
// persistent params
|
||||
bool shown_;
|
||||
|
||||
|
||||
void Handle(const nf7::File::Event& e) noexcept override {
|
||||
switch (e.type) {
|
||||
case nf7::File::Event::kReqFocus:
|
||||
SetFocus();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Update() noexcept override {
|
||||
if (std::exchange(set_focus_, false)) {
|
||||
ImGui::SetNextWindowFocus();
|
||||
shown_ = true;
|
||||
}
|
||||
if (!shown_) return;
|
||||
|
||||
onConfig();
|
||||
if (ImGui::Begin(id().c_str(), &shown_)) {
|
||||
onUpdate();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nf7::gui
|
||||
|
@ -109,6 +109,11 @@ class ObjBase : public nf7::FileBase,
|
||||
|
||||
if constexpr (HasWindow<T>) {
|
||||
win_.emplace(*this, T::kWindowTitle);
|
||||
win_->onConfig = []() {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSize({8*em, 8*em}, ImGuiCond_FirstUseEver);
|
||||
};
|
||||
win_->onUpdate = [this]() { mem_->UpdateWindow(fu_); };
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,22 +175,6 @@ class ObjBase : public nf7::FileBase,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Update() noexcept override {
|
||||
nf7::FileBase::Update();
|
||||
|
||||
if constexpr (HasWindow<T>) {
|
||||
if (win_->shownInCurrentFrame()) {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSize({8*em, 8*em}, ImGuiCond_FirstUseEver);
|
||||
}
|
||||
if (win_->Begin()) {
|
||||
mem_->UpdateWindow(fu_);
|
||||
}
|
||||
win_->End();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMenu() noexcept override {
|
||||
if (ImGui::BeginMenu("object management")) {
|
||||
if (ImGui::MenuItem("create", nullptr, false, !fu_)) {
|
||||
@ -211,7 +200,7 @@ class ObjBase : public nf7::FileBase,
|
||||
}
|
||||
if constexpr (HasWindow<T>) {
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem(T::kWindowTitle, nullptr, &win_->shown());
|
||||
win_->MenuItem();
|
||||
}
|
||||
}
|
||||
void UpdateTooltip() noexcept override {
|
||||
|
@ -109,7 +109,6 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
|
||||
};
|
||||
|
||||
Network(nf7::Env& env,
|
||||
const gui::Window* win = nullptr,
|
||||
ItemList&& items = {},
|
||||
nf7::NodeLinkStore&& links = {},
|
||||
Data&& d = {}) :
|
||||
@ -119,9 +118,15 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
|
||||
nf7::DirItem::kWidget),
|
||||
nf7::Node(nf7::Node::kNone),
|
||||
life_(*this),
|
||||
win_(*this, "Editor Node/Network", win),
|
||||
win_(*this, "Editor Node/Network"),
|
||||
items_(std::move(items)), links_(std::move(links)),
|
||||
mem_(std::move(d), *this) {
|
||||
win_.onConfig = []() {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSize({36*em, 36*em}, ImGuiCond_FirstUseEver);
|
||||
};
|
||||
win_.onUpdate = [this]() { NetworkEditor(); };
|
||||
|
||||
Sanitize();
|
||||
}
|
||||
~Network() noexcept {
|
||||
@ -142,7 +147,7 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
|
||||
items.push_back(std::make_unique<Item>(env, *item));
|
||||
}
|
||||
return std::make_unique<Network>(
|
||||
env, &win_, std::move(items), NodeLinkStore(links_));
|
||||
env, std::move(items), NodeLinkStore(links_));
|
||||
}
|
||||
|
||||
File* Find(std::string_view name) const noexcept;
|
||||
@ -243,6 +248,7 @@ class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Nod
|
||||
}
|
||||
|
||||
// gui
|
||||
void NetworkEditor() noexcept;
|
||||
void ItemAdder(const ImVec2&) noexcept;
|
||||
void Config() noexcept;
|
||||
|
||||
@ -964,8 +970,6 @@ void Network::Item::Watcher::Handle(const File::Event& ev) noexcept {
|
||||
void Network::Update() noexcept {
|
||||
nf7::FileBase::Update();
|
||||
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
// forget expired lambdas
|
||||
lambdas_running_.erase(
|
||||
std::remove_if(lambdas_running_.begin(), lambdas_running_.end(),
|
||||
@ -977,132 +981,13 @@ void Network::Update() noexcept {
|
||||
item->Update();
|
||||
}
|
||||
|
||||
// ---- editor window
|
||||
if (win_.shownInCurrentFrame()) {
|
||||
ImGui::SetNextWindowSize({36*em, 36*em}, ImGuiCond_FirstUseEver);
|
||||
}
|
||||
if (win_.Begin()) {
|
||||
// ---- editor window / toolbar
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
// ---- editor window / toolbar / attached lambda combo
|
||||
const auto current_lambda =
|
||||
!lambda_? "(unselected)"s:
|
||||
lambda_->depth() == 0? "(isolated)"s:
|
||||
nf7::gui::GetContextDisplayName(*lambda_);
|
||||
if (ImGui::BeginCombo("##lambda", current_lambda.c_str())) {
|
||||
if (lambda_) {
|
||||
if (ImGui::Selectable("detach current lambda")) {
|
||||
AttachLambda(nullptr);
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
for (const auto& wptr : lambdas_running_) {
|
||||
auto ptr = wptr.lock();
|
||||
if (!ptr) continue;
|
||||
|
||||
const auto name = nf7::gui::GetContextDisplayName(*ptr);
|
||||
if (ImGui::Selectable(name.c_str(), ptr == lambda_)) {
|
||||
AttachLambda(nullptr);
|
||||
lambda_ = ptr;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted("call stack:");
|
||||
ImGui::Indent();
|
||||
nf7::gui::ContextStack(*ptr);
|
||||
ImGui::Unindent();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
if (lambdas_running_.size() == 0) {
|
||||
ImGui::TextDisabled("no running lambda found...");
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
// ---- editor window / canvas
|
||||
if (ImGui::BeginChild("canvas", {0, 0}, false, ImGuiWindowFlags_NoMove)) {
|
||||
canvas_pos_ = ImGui::GetCursorScreenPos();
|
||||
ImNodes::BeginCanvas(&canvas_);
|
||||
|
||||
// update child nodes
|
||||
auto ed = Network::Editor {*this};
|
||||
for (const auto& item : items_) {
|
||||
item->UpdateNode(ed);
|
||||
}
|
||||
|
||||
// handle existing links
|
||||
for (const auto& lk : links_.items()) {
|
||||
const auto src_id = reinterpret_cast<void*>(lk.src_id);
|
||||
const auto dst_id = reinterpret_cast<void*>(lk.dst_id);
|
||||
const auto src_name = lk.src_name.c_str();
|
||||
const auto dst_name = lk.dst_name.c_str();
|
||||
if (!ImNodes::Connection(dst_id, dst_name, src_id, src_name)) {
|
||||
ExecUnlink(lk);
|
||||
}
|
||||
}
|
||||
|
||||
// handle new link
|
||||
void* src_ptr;
|
||||
const char* src_name;
|
||||
void* dst_ptr;
|
||||
const char* dst_name;
|
||||
if (ImNodes::GetNewConnection(&dst_ptr, &dst_name, &src_ptr, &src_name)) {
|
||||
ExecLink({
|
||||
.src_id = reinterpret_cast<ItemId>(src_ptr),
|
||||
.src_name = src_name,
|
||||
.dst_id = reinterpret_cast<ItemId>(dst_ptr),
|
||||
.dst_name = dst_name,
|
||||
});
|
||||
}
|
||||
ImNodes::EndCanvas();
|
||||
|
||||
// popup menu for canvas
|
||||
constexpr auto kFlags =
|
||||
ImGuiPopupFlags_MouseButtonRight |
|
||||
ImGuiPopupFlags_NoOpenOverExistingPopup;
|
||||
if (ImGui::BeginPopupContextWindow(nullptr, kFlags)) {
|
||||
const auto pos =
|
||||
GetCanvasPosFromScreenPos(ImGui::GetMousePosOnOpeningCurrentPopup());
|
||||
if (ImGui::BeginMenu("add")) {
|
||||
ItemAdder(pos);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("undo", nullptr, false, !!history_.prev())) {
|
||||
UnDo();
|
||||
}
|
||||
if (ImGui::MenuItem("redo", nullptr, false, !!history_.next())) {
|
||||
ReDo();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("reset canvas zoom")) {
|
||||
canvas_.Zoom = 1.f;
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("config")) {
|
||||
Config();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
win_.End();
|
||||
|
||||
// squash queued commands
|
||||
if (history_.Squash()) {
|
||||
Touch();
|
||||
}
|
||||
}
|
||||
void Network::UpdateMenu() noexcept {
|
||||
if (ImGui::MenuItem("Editor", nullptr, &win_.shown()) && win_.shown()) {
|
||||
win_.SetFocus();
|
||||
}
|
||||
win_.MenuItem();
|
||||
if (ImGui::BeginMenu("config")) {
|
||||
Config();
|
||||
ImGui::EndMenu();
|
||||
@ -1119,61 +1004,119 @@ void Network::UpdateWidget() noexcept {
|
||||
Config();
|
||||
}
|
||||
|
||||
void Network::Item::UpdateNode(Node::Editor& ed) noexcept {
|
||||
assert(owner_);
|
||||
ImGui::PushID(node_);
|
||||
|
||||
const auto id = reinterpret_cast<void*>(id_);
|
||||
if (ImNodes::BeginNode(id, &pos_, &select_)) {
|
||||
if (node_->flags() & nf7::Node::kCustomNode) {
|
||||
node_->UpdateNode(ed);
|
||||
} else {
|
||||
ImGui::TextUnformatted(file_->type().name().c_str());
|
||||
nf7::gui::NodeInputSockets(node_->GetInputs());
|
||||
ImGui::SameLine();
|
||||
nf7::gui::NodeOutputSockets(node_->GetOutputs());
|
||||
void Network::NetworkEditor() noexcept {
|
||||
// ---- editor window / toolbar
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
// ---- editor window / toolbar / attached lambda combo
|
||||
const auto current_lambda =
|
||||
!lambda_? "(unselected)"s:
|
||||
lambda_->depth() == 0? "(isolated)"s:
|
||||
nf7::gui::GetContextDisplayName(*lambda_);
|
||||
if (ImGui::BeginCombo("##lambda", current_lambda.c_str())) {
|
||||
if (lambda_) {
|
||||
if (ImGui::Selectable("detach current lambda")) {
|
||||
AttachLambda(nullptr);
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
for (const auto& wptr : lambdas_running_) {
|
||||
auto ptr = wptr.lock();
|
||||
if (!ptr) continue;
|
||||
|
||||
const auto name = nf7::gui::GetContextDisplayName(*ptr);
|
||||
if (ImGui::Selectable(name.c_str(), ptr == lambda_)) {
|
||||
AttachLambda(nullptr);
|
||||
lambda_ = ptr;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted("call stack:");
|
||||
ImGui::Indent();
|
||||
nf7::gui::ContextStack(*ptr);
|
||||
ImGui::Unindent();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
if (lambdas_running_.size() == 0) {
|
||||
ImGui::TextDisabled("no running lambda found...");
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
ImNodes::EndNode();
|
||||
ImGui::EndGroup();
|
||||
|
||||
const bool moved =
|
||||
pos_.x != prev_pos_.x || pos_.y != prev_pos_.y;
|
||||
if (moved && !ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
owner_->history_.Add(std::make_unique<Item::MoveCommand>(*this, prev_pos_));
|
||||
prev_pos_ = pos_;
|
||||
}
|
||||
// ---- editor window / canvas
|
||||
if (ImGui::BeginChild("canvas", {0, 0}, false, ImGuiWindowFlags_NoMove)) {
|
||||
canvas_pos_ = ImGui::GetCursorScreenPos();
|
||||
ImNodes::BeginCanvas(&canvas_);
|
||||
|
||||
constexpr auto kFlags =
|
||||
ImGuiPopupFlags_MouseButtonRight |
|
||||
ImGuiPopupFlags_NoOpenOverExistingPopup;
|
||||
if (ImGui::BeginPopupContextItem(nullptr, kFlags)) {
|
||||
const auto pos =
|
||||
owner_->GetCanvasPosFromScreenPos(
|
||||
ImGui::GetMousePosOnOpeningCurrentPopup());
|
||||
if (ImGui::MenuItem("remove")) {
|
||||
owner_->ExecRemoveItem(id_);
|
||||
// update child nodes
|
||||
auto ed = Network::Editor {*this};
|
||||
for (const auto& item : items_) {
|
||||
item->UpdateNode(ed);
|
||||
}
|
||||
if (ImGui::MenuItem("clone")) {
|
||||
owner_->ExecAddItem(
|
||||
std::make_unique<Item>(owner_->next_++, file_->Clone(env())), pos);
|
||||
|
||||
// handle existing links
|
||||
for (const auto& lk : links_.items()) {
|
||||
const auto src_id = reinterpret_cast<void*>(lk.src_id);
|
||||
const auto dst_id = reinterpret_cast<void*>(lk.dst_id);
|
||||
const auto src_name = lk.src_name.c_str();
|
||||
const auto dst_name = lk.dst_name.c_str();
|
||||
if (!ImNodes::Connection(dst_id, dst_name, src_id, src_name)) {
|
||||
ExecUnlink(lk);
|
||||
}
|
||||
}
|
||||
if (node_->flags() & nf7::Node::kMenu_DirItem) {
|
||||
|
||||
// handle new link
|
||||
void* src_ptr;
|
||||
const char* src_name;
|
||||
void* dst_ptr;
|
||||
const char* dst_name;
|
||||
if (ImNodes::GetNewConnection(&dst_ptr, &dst_name, &src_ptr, &src_name)) {
|
||||
ExecLink({
|
||||
.src_id = reinterpret_cast<ItemId>(src_ptr),
|
||||
.src_name = src_name,
|
||||
.dst_id = reinterpret_cast<ItemId>(dst_ptr),
|
||||
.dst_name = dst_name,
|
||||
});
|
||||
}
|
||||
ImNodes::EndCanvas();
|
||||
|
||||
// popup menu for canvas
|
||||
constexpr auto kFlags =
|
||||
ImGuiPopupFlags_MouseButtonRight |
|
||||
ImGuiPopupFlags_NoOpenOverExistingPopup;
|
||||
if (ImGui::BeginPopupContextWindow(nullptr, kFlags)) {
|
||||
const auto pos =
|
||||
GetCanvasPosFromScreenPos(ImGui::GetMousePosOnOpeningCurrentPopup());
|
||||
if (ImGui::BeginMenu("add")) {
|
||||
ItemAdder(pos);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::Separator();
|
||||
auto dir = file_->interface<nf7::DirItem>();
|
||||
assert(dir);
|
||||
dir->UpdateMenu();
|
||||
}
|
||||
if (node_->flags() & nf7::Node::kMenu) {
|
||||
if (ImGui::MenuItem("undo", nullptr, false, !!history_.prev())) {
|
||||
UnDo();
|
||||
}
|
||||
if (ImGui::MenuItem("redo", nullptr, false, !!history_.next())) {
|
||||
ReDo();
|
||||
}
|
||||
ImGui::Separator();
|
||||
node_->UpdateMenu(ed);
|
||||
if (ImGui::MenuItem("reset canvas zoom")) {
|
||||
canvas_.Zoom = 1.f;
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("config")) {
|
||||
Config();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
|
||||
void Network::ItemAdder(const ImVec2& pos) noexcept {
|
||||
static const nf7::File::TypeInfo* type;
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
@ -1241,6 +1184,61 @@ void Network::Config() noexcept {
|
||||
}
|
||||
|
||||
|
||||
void Network::Item::UpdateNode(Node::Editor& ed) noexcept {
|
||||
assert(owner_);
|
||||
ImGui::PushID(node_);
|
||||
|
||||
const auto id = reinterpret_cast<void*>(id_);
|
||||
if (ImNodes::BeginNode(id, &pos_, &select_)) {
|
||||
if (node_->flags() & nf7::Node::kCustomNode) {
|
||||
node_->UpdateNode(ed);
|
||||
} else {
|
||||
ImGui::TextUnformatted(file_->type().name().c_str());
|
||||
nf7::gui::NodeInputSockets(node_->GetInputs());
|
||||
ImGui::SameLine();
|
||||
nf7::gui::NodeOutputSockets(node_->GetOutputs());
|
||||
}
|
||||
}
|
||||
ImNodes::EndNode();
|
||||
|
||||
const bool moved =
|
||||
pos_.x != prev_pos_.x || pos_.y != prev_pos_.y;
|
||||
if (moved && !ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
owner_->history_.Add(std::make_unique<Item::MoveCommand>(*this, prev_pos_));
|
||||
prev_pos_ = pos_;
|
||||
}
|
||||
|
||||
constexpr auto kFlags =
|
||||
ImGuiPopupFlags_MouseButtonRight |
|
||||
ImGuiPopupFlags_NoOpenOverExistingPopup;
|
||||
if (ImGui::BeginPopupContextItem(nullptr, kFlags)) {
|
||||
const auto pos =
|
||||
owner_->GetCanvasPosFromScreenPos(
|
||||
ImGui::GetMousePosOnOpeningCurrentPopup());
|
||||
if (ImGui::MenuItem("remove")) {
|
||||
owner_->ExecRemoveItem(id_);
|
||||
}
|
||||
if (ImGui::MenuItem("clone")) {
|
||||
owner_->ExecAddItem(
|
||||
std::make_unique<Item>(owner_->next_++, file_->Clone(env())), pos);
|
||||
}
|
||||
if (node_->flags() & nf7::Node::kMenu_DirItem) {
|
||||
ImGui::Separator();
|
||||
auto dir = file_->interface<nf7::DirItem>();
|
||||
assert(dir);
|
||||
dir->UpdateMenu();
|
||||
}
|
||||
if (node_->flags() & nf7::Node::kMenu) {
|
||||
ImGui::Separator();
|
||||
node_->UpdateMenu(ed);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
|
||||
void Network::Initiator::UpdateNode(nf7::Node::Editor& ed) noexcept {
|
||||
ImGui::TextUnformatted("INITIATOR");
|
||||
|
||||
|
@ -67,14 +67,14 @@ class TL final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
|
||||
TL(nf7::Env& env,
|
||||
std::vector<std::unique_ptr<Layer>>&& layers = {},
|
||||
ItemId next = 1,
|
||||
const nf7::gui::Window* win = nullptr) noexcept :
|
||||
ItemId next = 1) noexcept :
|
||||
nf7::FileBase(kType, env),
|
||||
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kWidget),
|
||||
nf7::DirItem(nf7::DirItem::kMenu),
|
||||
nf7::Node(nf7::Node::kMenu_DirItem),
|
||||
life_(*this), log_(*this),
|
||||
layers_(std::move(layers)), next_(next),
|
||||
win_(*this, "Timeline Editor", win), tl_("timeline") {
|
||||
win_(*this, "Timeline Editor"), tl_("timeline") {
|
||||
win_.onUpdate = [this]() { TimelineEditor(); };
|
||||
}
|
||||
~TL() noexcept {
|
||||
history_.Clear();
|
||||
@ -103,7 +103,6 @@ class TL final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
void Handle(const nf7::File::Event& ev) noexcept;
|
||||
void Update() noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateWidget() noexcept override;
|
||||
|
||||
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<nf7::DirItem, nf7::Node>(t).Select(this);
|
||||
@ -175,8 +174,8 @@ class TL final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
void AttachLambda(const std::shared_ptr<TL::Lambda>&) noexcept;
|
||||
|
||||
// gui
|
||||
void EditorWindow() noexcept;
|
||||
void ParamPanelWindow() noexcept;
|
||||
void TimelineEditor() noexcept;
|
||||
void ParamPanel() noexcept;
|
||||
void LambdaSelector() noexcept;
|
||||
void ItemAdder() noexcept;
|
||||
|
||||
@ -1089,8 +1088,10 @@ std::unique_ptr<nf7::File> TL::Clone(nf7::Env& env) const noexcept {
|
||||
std::vector<std::unique_ptr<TL::Layer>> layers;
|
||||
layers.reserve(layers_.size());
|
||||
ItemId next = 1;
|
||||
for (const auto& layer : layers_) layers.push_back(layer->Clone(env, next));
|
||||
return std::make_unique<TL>(env, std::move(layers), next, &win_);
|
||||
for (const auto& layer : layers_) {
|
||||
layers.push_back(layer->Clone(env, next));
|
||||
}
|
||||
return std::make_unique<TL>(env, std::move(layers), next);
|
||||
}
|
||||
void TL::Handle(const Event& ev) noexcept {
|
||||
nf7::FileBase::Handle(ev);
|
||||
@ -1127,188 +1128,174 @@ void TL::Handle(const Event& ev) noexcept {
|
||||
void TL::Update() noexcept {
|
||||
nf7::FileBase::Update();
|
||||
|
||||
// display param panel window
|
||||
if (win_.shown()) {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
const auto id = nf7::gui::Window::ConcatId(*this, "Parameter Panel");
|
||||
|
||||
if (std::exchange(param_panel_request_focus_, false)) {
|
||||
ImGui::SetNextWindowFocus();
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize({16*em, 16*em}, ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin(id.c_str())) {
|
||||
ParamPanel();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// update children
|
||||
for (const auto& layer : layers_) {
|
||||
for (const auto& item : layer->items()) {
|
||||
item->file().Update();
|
||||
}
|
||||
}
|
||||
|
||||
EditorWindow();
|
||||
ParamPanelWindow();
|
||||
|
||||
// squash queued commands
|
||||
if (history_.Squash()) {
|
||||
env().ExecMain(std::make_shared<nf7::GenericContext>(*this),
|
||||
[this]() { Touch(); });
|
||||
}
|
||||
}
|
||||
void TL::UpdateMenu() noexcept {
|
||||
if (ImGui::MenuItem("editor", nullptr, &win_.shown()) && win_.shown()) {
|
||||
win_.SetFocus();
|
||||
}
|
||||
}
|
||||
void TL::UpdateWidget() noexcept {
|
||||
ImGui::TextUnformatted("Sequencer/Timeline");
|
||||
if (ImGui::Button("Editor")) {
|
||||
win_.SetFocus();
|
||||
}
|
||||
win_.MenuItem();
|
||||
}
|
||||
|
||||
void TL::EditorWindow() noexcept {
|
||||
if (win_.shownInCurrentFrame()) {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSizeConstraints({32*em, 16*em}, {1e8, 1e8});
|
||||
}
|
||||
if (win_.Begin()) {
|
||||
LambdaSelector();
|
||||
void TL::TimelineEditor() noexcept {
|
||||
LambdaSelector();
|
||||
|
||||
// timeline
|
||||
if (tl_.Begin()) {
|
||||
// layer headers
|
||||
for (size_t i = 0; i < layers_.size(); ++i) {
|
||||
auto& layer = layers_[i];
|
||||
tl_.NextLayerHeader(layer.get(), layer->height());
|
||||
ImGui::PushID(layer.get());
|
||||
layer->UpdateHeader(i);
|
||||
ImGui::PopID();
|
||||
}
|
||||
// timeline
|
||||
if (tl_.Begin()) {
|
||||
// layer headers
|
||||
for (size_t i = 0; i < layers_.size(); ++i) {
|
||||
auto& layer = layers_[i];
|
||||
tl_.NextLayerHeader(layer.get(), layer->height());
|
||||
ImGui::PushID(layer.get());
|
||||
layer->UpdateHeader(i);
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (tl_.BeginBody()) {
|
||||
// context menu on timeline
|
||||
if (ImGui::BeginPopupContextWindow()) {
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
action_time_ = tl_.mouseTime();
|
||||
action_layer_ = 0;
|
||||
if (auto layer = reinterpret_cast<TL::Layer*>(tl_.mouseLayer())) {
|
||||
action_layer_ = layer->index();
|
||||
}
|
||||
if (tl_.BeginBody()) {
|
||||
// context menu on timeline
|
||||
if (ImGui::BeginPopupContextWindow()) {
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
action_time_ = tl_.mouseTime();
|
||||
action_layer_ = 0;
|
||||
if (auto layer = reinterpret_cast<TL::Layer*>(tl_.mouseLayer())) {
|
||||
action_layer_ = layer->index();
|
||||
}
|
||||
if (action_layer_ < layers_.size()) {
|
||||
if (ImGui::BeginMenu("add new item")) {
|
||||
ItemAdder();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
if (selected_.size()) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("deselect")) {
|
||||
Deselect();
|
||||
}
|
||||
}
|
||||
if (action_layer_ < layers_.size()) {
|
||||
if (ImGui::BeginMenu("add new item")) {
|
||||
ItemAdder();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
if (selected_.size()) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("undo", nullptr, false, !!history_.prev())) {
|
||||
ExecUnDo();
|
||||
}
|
||||
if (ImGui::MenuItem("redo", nullptr, false, !!history_.next())) {
|
||||
ExecReDo();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// layer body
|
||||
for (auto& layer : layers_) {
|
||||
tl_.NextLayer(layer.get(), layer->height());
|
||||
for (auto& item : layer->items()) {
|
||||
const auto& t = item->displayTiming();
|
||||
const bool select = selected_.contains(item.get());
|
||||
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_FrameBg, 0.3f));
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_Border,
|
||||
ImGui::GetColorU32(select? ImGuiCol_FrameBgActive: ImGuiCol_Border));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 2);
|
||||
const bool shown = tl_.BeginItem(item.get(), t.begin(), t.end());
|
||||
ImGui::PopStyleVar(1);
|
||||
ImGui::PopStyleColor(2);
|
||||
if (shown) {
|
||||
item->Update();
|
||||
}
|
||||
tl_.EndItem();
|
||||
|
||||
if (ImGui::MenuItem("deselect")) {
|
||||
Deselect();
|
||||
}
|
||||
}
|
||||
}
|
||||
tl_.EndBody();
|
||||
|
||||
// mouse curosr
|
||||
constexpr auto kFlags =
|
||||
ImGuiHoveredFlags_ChildWindows |
|
||||
ImGuiHoveredFlags_AllowWhenBlockedByPopup;
|
||||
if (ImGui::IsWindowHovered(kFlags)) {
|
||||
tl_.Cursor(
|
||||
"mouse",
|
||||
tl_.GetTimeFromScreenX(ImGui::GetMousePos().x),
|
||||
ImGui::GetColorU32(ImGuiCol_TextDisabled, .5f));
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("undo", nullptr, false, !!history_.prev())) {
|
||||
ExecUnDo();
|
||||
}
|
||||
if (ImGui::MenuItem("redo", nullptr, false, !!history_.next())) {
|
||||
ExecReDo();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// frame cursor
|
||||
tl_.Cursor("cursor", cursor_, ImGui::GetColorU32(ImGuiCol_Text, .5f));
|
||||
// layer body
|
||||
for (auto& layer : layers_) {
|
||||
tl_.NextLayer(layer.get(), layer->height());
|
||||
for (auto& item : layer->items()) {
|
||||
const auto& t = item->displayTiming();
|
||||
const bool select = selected_.contains(item.get());
|
||||
|
||||
// running sessions
|
||||
if (lambda_) {
|
||||
const auto now = nf7::Env::Clock::now();
|
||||
for (auto& wss : lambda_->sessions()) {
|
||||
auto ss = wss.lock();
|
||||
if (!ss || ss->done()) continue;
|
||||
|
||||
const auto elapsed =
|
||||
static_cast<float>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - ss->lastActive()).count()) / 1000;
|
||||
|
||||
const auto alpha = 1.f - std::clamp(elapsed, 0.f, 1.f)*0.6f;
|
||||
const auto color = IM_COL32(255, 0, 0, static_cast<uint8_t>(alpha*255));
|
||||
|
||||
tl_.Cursor("S", ss->time(), color);
|
||||
if (ss->layer() > 0) {
|
||||
tl_.Arrow(ss->time(), ss->layer()-1, color);
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_FrameBg, 0.3f));
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_Border,
|
||||
ImGui::GetColorU32(select? ImGuiCol_FrameBgActive: ImGuiCol_Border));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 2);
|
||||
const bool shown = tl_.BeginItem(item.get(), t.begin(), t.end());
|
||||
ImGui::PopStyleVar(1);
|
||||
ImGui::PopStyleColor(2);
|
||||
if (shown) {
|
||||
item->Update();
|
||||
}
|
||||
tl_.EndItem();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
HandleTimelineAction();
|
||||
}
|
||||
tl_.End();
|
||||
tl_.EndBody();
|
||||
|
||||
// key bindings
|
||||
const bool focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
||||
if (focused && !ImGui::IsAnyItemFocused()) {
|
||||
if (!lambda_ || lambda_->depth() == 0) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
|
||||
if (cursor_ > 0) MoveCursorTo(cursor_-1);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
|
||||
MoveCursorTo(cursor_+1);
|
||||
// mouse curosr
|
||||
constexpr auto kFlags =
|
||||
ImGuiHoveredFlags_ChildWindows |
|
||||
ImGuiHoveredFlags_AllowWhenBlockedByPopup;
|
||||
if (ImGui::IsWindowHovered(kFlags)) {
|
||||
tl_.Cursor(
|
||||
"mouse",
|
||||
tl_.GetTimeFromScreenX(ImGui::GetMousePos().x),
|
||||
ImGui::GetColorU32(ImGuiCol_TextDisabled, .5f));
|
||||
}
|
||||
|
||||
// frame cursor
|
||||
tl_.Cursor("cursor", cursor_, ImGui::GetColorU32(ImGuiCol_Text, .5f));
|
||||
|
||||
// running sessions
|
||||
if (lambda_) {
|
||||
const auto now = nf7::Env::Clock::now();
|
||||
for (auto& wss : lambda_->sessions()) {
|
||||
auto ss = wss.lock();
|
||||
if (!ss || ss->done()) continue;
|
||||
|
||||
const auto elapsed =
|
||||
static_cast<float>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - ss->lastActive()).count()) / 1000;
|
||||
|
||||
const auto alpha = 1.f - std::clamp(elapsed, 0.f, 1.f)*0.6f;
|
||||
const auto color = IM_COL32(255, 0, 0, static_cast<uint8_t>(alpha*255));
|
||||
|
||||
tl_.Cursor("S", ss->time(), color);
|
||||
if (ss->layer() > 0) {
|
||||
tl_.Arrow(ss->time(), ss->layer()-1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandleTimelineAction();
|
||||
}
|
||||
tl_.End();
|
||||
|
||||
// key bindings
|
||||
const bool focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
||||
if (focused && !ImGui::IsAnyItemFocused()) {
|
||||
if (!lambda_ || lambda_->depth() == 0) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
|
||||
if (cursor_ > 0) MoveCursorTo(cursor_-1);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
|
||||
MoveCursorTo(cursor_+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
win_.End();
|
||||
}
|
||||
void TL::ParamPanelWindow() noexcept {
|
||||
if (!win_.shown()) return;
|
||||
|
||||
const auto name = abspath().Stringify() + " | Parameter Panel";
|
||||
|
||||
if (std::exchange(param_panel_request_focus_, false)) {
|
||||
ImGui::SetNextWindowFocus();
|
||||
}
|
||||
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSize({16*em, 16*em}, ImGuiCond_FirstUseEver);
|
||||
|
||||
if (ImGui::Begin(name.c_str())) {
|
||||
if (auto item = param_panel_target_) {
|
||||
if (item->seq().flags() & Sequencer::kParamPanel) {
|
||||
TL::Editor ed {*item};
|
||||
item->seq().UpdateParamPanel(ed);
|
||||
} else {
|
||||
ImGui::TextUnformatted("item doesn't have parameter panel");
|
||||
}
|
||||
void TL::ParamPanel() noexcept {
|
||||
if (auto item = param_panel_target_) {
|
||||
if (item->seq().flags() & Sequencer::kParamPanel) {
|
||||
TL::Editor ed {*item};
|
||||
item->seq().UpdateParamPanel(ed);
|
||||
} else {
|
||||
ImGui::TextUnformatted("no item selected");
|
||||
ImGui::TextUnformatted("item doesn't have parameter panel");
|
||||
}
|
||||
} else {
|
||||
ImGui::TextUnformatted("no item selected");
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
void TL::LambdaSelector() noexcept {
|
||||
const auto current_lambda =
|
||||
|
@ -36,13 +36,18 @@ class Dir final : public nf7::FileBase,
|
||||
|
||||
using ItemMap = std::map<std::string, std::unique_ptr<File>>;
|
||||
|
||||
Dir(nf7::Env& env, ItemMap&& items = {}, const gui::Window* src = nullptr) noexcept :
|
||||
Dir(nf7::Env& env, 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, "TreeView System/Dir", src) {
|
||||
items_(std::move(items)), win_(*this, "Tree View") {
|
||||
win_.onConfig = []() {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSize({8*em, 8*em}, ImGuiCond_FirstUseEver);
|
||||
};
|
||||
win_.onUpdate = [this]() { TreeView(); };
|
||||
}
|
||||
|
||||
Dir(nf7::Deserializer& ar) : Dir(ar.env()) {
|
||||
@ -113,23 +118,21 @@ class Dir final : public nf7::FileBase,
|
||||
|
||||
void Handle(const Event& ev) noexcept override {
|
||||
nf7::FileBase::Handle(ev);
|
||||
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
// force to show window if this is the root
|
||||
if (name() == "$") {
|
||||
win_.shown() = true;
|
||||
win_.Show();
|
||||
}
|
||||
for (const auto& item : items_) item.second->MoveUnder(*this, item.first);
|
||||
break;
|
||||
return;
|
||||
case Event::kRemove:
|
||||
for (const auto& item : items_) item.second->Isolate();
|
||||
break;
|
||||
case Event::kReqFocus:
|
||||
win_.SetFocus();
|
||||
break;
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,44 +157,20 @@ class Dir final : public nf7::FileBase,
|
||||
}
|
||||
|
||||
// imgui widgets
|
||||
void TreeView() noexcept;
|
||||
void ItemAdder() noexcept;
|
||||
void ItemRenamer(const std::string& name) noexcept;
|
||||
bool ValidateName(const std::string& name) noexcept;
|
||||
};
|
||||
|
||||
void Dir::Update() noexcept {
|
||||
nf7::FileBase::Update();
|
||||
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
// update children
|
||||
for (const auto& item : items_) {
|
||||
ImGui::PushID(item.second.get());
|
||||
item.second->Update();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
// tree view window
|
||||
if (win_.shownInCurrentFrame()) {
|
||||
ImGui::SetNextWindowSize({8*em, 8*em}, ImGuiCond_FirstUseEver);
|
||||
}
|
||||
if (win_.Begin()) {
|
||||
if (ImGui::BeginPopupContextWindow()) {
|
||||
UpdateMenu();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
UpdateTree();
|
||||
|
||||
if (nf7::gui::dnd::IsFirstAccept()) {
|
||||
ImGui::SetCursorPos({0, 0});
|
||||
ImGui::Dummy(ImGui::GetContentRegionAvail());
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
UpdateDragDropTarget();
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
win_.End();
|
||||
nf7::FileBase::Update();
|
||||
}
|
||||
void Dir::UpdateTree() noexcept {
|
||||
for (const auto& item : items_) {
|
||||
@ -315,7 +294,7 @@ void Dir::UpdateMenu() noexcept {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("TreeView", nullptr, &win_.shown());
|
||||
win_.MenuItem();
|
||||
}
|
||||
void Dir::UpdateTooltip() noexcept {
|
||||
ImGui::Text("children: %zu", items_.size());
|
||||
@ -348,6 +327,23 @@ try {
|
||||
}
|
||||
|
||||
|
||||
void Dir::TreeView() noexcept {
|
||||
if (ImGui::BeginPopupContextWindow()) {
|
||||
UpdateMenu();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
UpdateTree();
|
||||
|
||||
if (nf7::gui::dnd::IsFirstAccept()) {
|
||||
ImGui::SetCursorPos({0, 0});
|
||||
ImGui::Dummy(ImGui::GetContentRegionAvail());
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
UpdateDragDropTarget();
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dir::ItemAdder() noexcept {
|
||||
static const nf7::File::TypeInfo* type;
|
||||
static std::string name;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
@ -34,7 +35,7 @@ using namespace std::literals;
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Logger final : public nf7::File,
|
||||
class Logger final : public nf7::FileBase,
|
||||
public nf7::DirItem {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Logger> kType = {
|
||||
@ -110,13 +111,17 @@ class Logger final : public nf7::File,
|
||||
};
|
||||
|
||||
Logger(nf7::Env& env, Data&& d = {}) noexcept :
|
||||
nf7::File(kType, env), DirItem(DirItem::kMenu),
|
||||
nf7::FileBase(kType, env), nf7::DirItem(DirItem::kMenu),
|
||||
mem_(std::move(d), *this), win_(*this, "Log View") {
|
||||
win_.shown() = true;
|
||||
|
||||
mem_.onCommit = mem_.onRestore = [this]() {
|
||||
store_->param(mem_.data());
|
||||
};
|
||||
|
||||
win_.onConfig = []() {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::SetNextWindowSize({48*em, 16*em}, ImGuiCond_FirstUseEver);
|
||||
};
|
||||
win_.onUpdate = [this]() { LogView(); };
|
||||
}
|
||||
|
||||
Logger(nf7::Deserializer& ar) : Logger(ar.env()) {
|
||||
@ -142,7 +147,6 @@ class Logger final : public nf7::File,
|
||||
}
|
||||
}
|
||||
|
||||
void Update() noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateRowMenu(const Row&) noexcept;
|
||||
|
||||
@ -161,11 +165,13 @@ class Logger final : public nf7::File,
|
||||
nf7::gui::Window win_;
|
||||
|
||||
|
||||
// log record management
|
||||
void DropExceededRows() noexcept {
|
||||
if (rows_.size() <= mem_->max_rows) return;
|
||||
rows_.erase(rows_.begin(), rows_.end()-mem_->max_rows);
|
||||
}
|
||||
|
||||
// stringify
|
||||
std::string GetPathString(File::Id id) const noexcept
|
||||
try {
|
||||
return env().GetFileOrThrow(id).abspath().Stringify();
|
||||
@ -191,6 +197,9 @@ class Logger final : public nf7::File,
|
||||
return loc.file_name()+":"s+std::to_string(loc.line());
|
||||
}
|
||||
|
||||
// gui
|
||||
void LogView() noexcept;
|
||||
|
||||
|
||||
class ItemStore final : public nf7::Context,
|
||||
public nf7::Logger,
|
||||
@ -266,100 +275,13 @@ class Logger final : public nf7::File,
|
||||
};
|
||||
|
||||
|
||||
void Logger::Update() noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
if (win_.shownInCurrentFrame()) {
|
||||
ImGui::SetNextWindowSize({48*em, 16*em}, ImGuiCond_FirstUseEver);
|
||||
}
|
||||
if (win_.Begin()) {
|
||||
constexpr auto kTableFlags =
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_ContextMenuInBody |
|
||||
ImGuiTableFlags_SizingStretchProp |
|
||||
ImGuiTableFlags_ScrollY;
|
||||
if (ImGui::BeginTable("logs", 4, kTableFlags, ImGui::GetContentRegionAvail(), 0)) {
|
||||
const bool updated = store_->MoveItemsTo(*this);
|
||||
const bool autoscroll = updated && ImGui::GetScrollY() == ImGui::GetScrollMaxY();
|
||||
|
||||
ImGui::TableSetupColumn("level");
|
||||
ImGui::TableSetupColumn("msg");
|
||||
ImGui::TableSetupColumn("path");
|
||||
ImGui::TableSetupColumn("location");
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto& row : rows_) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(&row);
|
||||
|
||||
if (autoscroll && &row == &rows_.back()) {
|
||||
ImGui::SetScrollHereY();
|
||||
}
|
||||
|
||||
// level column
|
||||
if (ImGui::TableSetColumnIndex(0)) {
|
||||
constexpr auto kFlags =
|
||||
ImGuiSelectableFlags_SpanAllColumns |
|
||||
ImGuiSelectableFlags_AllowItemOverlap;
|
||||
ImGui::Selectable(row.level, false, kFlags);
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
UpdateRowMenu(row);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
// msg column
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(row.msg.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(row.msg.c_str());
|
||||
}
|
||||
}
|
||||
// path column
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(row.path.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(row.path.c_str());
|
||||
}
|
||||
}
|
||||
// location column
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(row.location.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text(row.location.c_str());
|
||||
for (auto ptr = row.ex; ptr;)
|
||||
try {
|
||||
ImGui::Bullet();
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (Exception& e) {
|
||||
e.UpdatePanic();
|
||||
ImGui::Spacing();
|
||||
ptr = e.reason();
|
||||
} catch (std::exception& e) {
|
||||
ImGui::Text("std::exception (%s)", e.what());
|
||||
ptr = nullptr;
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
win_.End();
|
||||
}
|
||||
void Logger::UpdateMenu() noexcept {
|
||||
if (ImGui::BeginMenu("config")) {
|
||||
nf7::gui::Config(mem_);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Log View", nullptr, &win_.shown());
|
||||
win_.MenuItem();
|
||||
}
|
||||
void Logger::UpdateRowMenu(const Row& row) noexcept {
|
||||
if (ImGui::MenuItem("copy as text")) {
|
||||
@ -372,5 +294,85 @@ void Logger::UpdateRowMenu(const Row& row) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::LogView() noexcept {
|
||||
constexpr auto kTableFlags =
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_ContextMenuInBody |
|
||||
ImGuiTableFlags_SizingStretchProp |
|
||||
ImGuiTableFlags_ScrollY;
|
||||
if (ImGui::BeginTable("logs", 4, kTableFlags, ImGui::GetContentRegionAvail(), 0)) {
|
||||
const bool updated = store_->MoveItemsTo(*this);
|
||||
const bool autoscroll = updated && ImGui::GetScrollY() == ImGui::GetScrollMaxY();
|
||||
|
||||
ImGui::TableSetupColumn("level");
|
||||
ImGui::TableSetupColumn("msg");
|
||||
ImGui::TableSetupColumn("path");
|
||||
ImGui::TableSetupColumn("location");
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto& row : rows_) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(&row);
|
||||
|
||||
if (autoscroll && &row == &rows_.back()) {
|
||||
ImGui::SetScrollHereY();
|
||||
}
|
||||
|
||||
// level column
|
||||
if (ImGui::TableSetColumnIndex(0)) {
|
||||
constexpr auto kFlags =
|
||||
ImGuiSelectableFlags_SpanAllColumns |
|
||||
ImGuiSelectableFlags_AllowItemOverlap;
|
||||
ImGui::Selectable(row.level, false, kFlags);
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
UpdateRowMenu(row);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
// msg column
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(row.msg.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(row.msg.c_str());
|
||||
}
|
||||
}
|
||||
// path column
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(row.path.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(row.path.c_str());
|
||||
}
|
||||
}
|
||||
// location column
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(row.location.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text(row.location.c_str());
|
||||
for (auto ptr = row.ex; ptr;)
|
||||
try {
|
||||
ImGui::Bullet();
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (Exception& e) {
|
||||
e.UpdatePanic();
|
||||
ImGui::Spacing();
|
||||
ptr = e.reason();
|
||||
} catch (std::exception& e) {
|
||||
ImGui::Text("std::exception (%s)", e.what());
|
||||
ptr = nullptr;
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
||||
|
@ -109,11 +109,12 @@ class Plot final : public nf7::FileBase,
|
||||
std::vector<Series> series;
|
||||
};
|
||||
|
||||
Plot(nf7::Env& env, const nf7::gui::Window* win = nullptr, Data&& data = {}) noexcept :
|
||||
Plot(nf7::Env& env, Data&& data = {}) noexcept :
|
||||
nf7::FileBase(kType, env),
|
||||
nf7::DirItem(nf7::DirItem::kMenu | nf7::DirItem::kWidget),
|
||||
nf7::DirItem(nf7::DirItem::kMenu),
|
||||
nf7::Node(nf7::Node::kNone),
|
||||
life_(*this), log_(*this), win_(*this, "Plot", win), mem_(std::move(data)) {
|
||||
life_(*this), log_(*this), win_(*this, "Plot"), mem_(std::move(data)) {
|
||||
win_.onUpdate = [this]() { PlotGraph(); };
|
||||
mem_.onRestore = mem_.onCommit = [this]() { BuildInputList(); };
|
||||
Sanitize();
|
||||
}
|
||||
@ -126,7 +127,7 @@ class Plot final : public nf7::FileBase,
|
||||
ar(win_, mem_->series);
|
||||
}
|
||||
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
|
||||
return std::make_unique<Plot>(env, &win_, Data {mem_.data()});
|
||||
return std::make_unique<Plot>(env, Data {mem_.data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
|
||||
@ -139,12 +140,8 @@ class Plot final : public nf7::FileBase,
|
||||
return {};
|
||||
}
|
||||
|
||||
void Update() noexcept override;
|
||||
void UpdateWidget() noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
|
||||
void UpdatePlot() noexcept;
|
||||
|
||||
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<
|
||||
nf7::DirItem, nf7::Memento, nf7::Node>(t).Select(this, &mem_);
|
||||
@ -161,6 +158,7 @@ class Plot final : public nf7::FileBase,
|
||||
std::vector<std::string> inputs_;
|
||||
|
||||
|
||||
// config management
|
||||
void Sanitize() {
|
||||
nf7::util::Uniq(mem_->series);
|
||||
mem_.CommitAmend();
|
||||
@ -172,6 +170,9 @@ class Plot final : public nf7::FileBase,
|
||||
inputs_.push_back(s.name);
|
||||
}
|
||||
}
|
||||
|
||||
// gui
|
||||
void PlotGraph() noexcept;
|
||||
};
|
||||
|
||||
|
||||
@ -237,22 +238,8 @@ std::shared_ptr<nf7::Node::Lambda> Plot::CreateLambda(
|
||||
}
|
||||
|
||||
|
||||
void Plot::Update() noexcept {
|
||||
nf7::FileBase::Update();
|
||||
|
||||
if (win_.Begin()) {
|
||||
UpdatePlot();
|
||||
}
|
||||
win_.End();
|
||||
}
|
||||
void Plot::UpdateWidget() noexcept {
|
||||
if (ImGui::Button("plot window")) {
|
||||
win_.SetFocus();
|
||||
}
|
||||
nf7::gui::Config(mem_);
|
||||
}
|
||||
void Plot::UpdateMenu() noexcept {
|
||||
ImGui::MenuItem("plot window", nullptr, &win_.shown());
|
||||
win_.MenuItem();
|
||||
|
||||
if (ImGui::BeginMenu("config")) {
|
||||
nf7::gui::Config(mem_);
|
||||
@ -260,7 +247,7 @@ void Plot::UpdateMenu() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void Plot::UpdatePlot() noexcept {
|
||||
void Plot::PlotGraph() noexcept {
|
||||
if (ImPlot::BeginPlot("##plot", ImGui::GetContentRegionAvail())) {
|
||||
ImPlot::SetupAxis(ImAxis_X1, "X", ImPlotAxisFlags_AutoFit);
|
||||
ImPlot::SetupAxis(ImAxis_Y1, "Y", ImPlotAxisFlags_AutoFit);
|
||||
|
Loading…
x
Reference in New Issue
Block a user