diff --git a/common/gui_window.hh b/common/gui_window.hh index 044b1f2..c938ffe 100644 --- a/common/gui_window.hh +++ b/common/gui_window.hh @@ -1,72 +1,57 @@ #pragma once -#include +#include #include #include +#include #include + #include #include #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 - 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 onConfig = [](){}; + std::function 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 diff --git a/file/gl_obj.cc b/file/gl_obj.cc index e6555cb..73ca1e5 100644 --- a/file/gl_obj.cc +++ b/file/gl_obj.cc @@ -109,6 +109,11 @@ class ObjBase : public nf7::FileBase, if constexpr (HasWindow) { 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) { - 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) { ImGui::Separator(); - ImGui::MenuItem(T::kWindowTitle, nullptr, &win_->shown()); + win_->MenuItem(); } } void UpdateTooltip() noexcept override { diff --git a/file/node_network.cc b/file/node_network.cc index 9e1cbd4..55c4d1f 100644 --- a/file/node_network.cc +++ b/file/node_network.cc @@ -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(env, *item)); } return std::make_unique( - 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(lk.src_id); - const auto dst_id = reinterpret_cast(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(src_ptr), - .src_name = src_name, - .dst_id = reinterpret_cast(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(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(*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(owner_->next_++, file_->Clone(env())), pos); + + // handle existing links + for (const auto& lk : links_.items()) { + const auto src_id = reinterpret_cast(lk.src_id); + const auto dst_id = reinterpret_cast(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(src_ptr), + .src_name = src_name, + .dst_id = reinterpret_cast(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(); - 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(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(*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(owner_->next_++, file_->Clone(env())), pos); + } + if (node_->flags() & nf7::Node::kMenu_DirItem) { + ImGui::Separator(); + auto dir = file_->interface(); + 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"); diff --git a/file/sequencer_timeline.cc b/file/sequencer_timeline.cc index 698f08c..b80a001 100644 --- a/file/sequencer_timeline.cc +++ b/file/sequencer_timeline.cc @@ -67,14 +67,14 @@ class TL final : public nf7::FileBase, public nf7::DirItem, public nf7::Node { TL(nf7::Env& env, std::vector>&& 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(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&) 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 TL::Clone(nf7::Env& env) const noexcept { std::vector> layers; layers.reserve(layers_.size()); ItemId next = 1; - for (const auto& layer : layers_) layers.push_back(layer->Clone(env, next)); - return std::make_unique(env, std::move(layers), next, &win_); + for (const auto& layer : layers_) { + layers.push_back(layer->Clone(env, next)); + } + return std::make_unique(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(*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_.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_.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( - std::chrono::duration_cast( - 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(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( + std::chrono::duration_cast( + 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(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 = diff --git a/file/system_dir.cc b/file/system_dir.cc index 8f388d9..692efa0 100644 --- a/file/system_dir.cc +++ b/file/system_dir.cc @@ -36,13 +36,18 @@ class Dir final : public nf7::FileBase, using ItemMap = std::map>; - 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; diff --git a/file/system_logger.cc b/file/system_logger.cc index 34e4d7e..24f4754 100644 --- a/file/system_logger.cc +++ b/file/system_logger.cc @@ -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 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 diff --git a/file/value_plot.cc b/file/value_plot.cc index 7624e39..10efa87 100644 --- a/file/value_plot.cc +++ b/file/value_plot.cc @@ -109,11 +109,12 @@ class Plot final : public nf7::FileBase, std::vector 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 Clone(nf7::Env& env) const noexcept override { - return std::make_unique(env, &win_, Data {mem_.data()}); + return std::make_unique(env, Data {mem_.data()}); } std::shared_ptr 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 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 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);