diff options
Diffstat (limited to 'app/source/Cplt/UI/UI_Workflows.cpp')
-rw-r--r-- | app/source/Cplt/UI/UI_Workflows.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/app/source/Cplt/UI/UI_Workflows.cpp b/app/source/Cplt/UI/UI_Workflows.cpp new file mode 100644 index 0000000..5eea53a --- /dev/null +++ b/app/source/Cplt/UI/UI_Workflows.cpp @@ -0,0 +1,293 @@ +#include "UI.hpp" + +#include <Cplt/Model/GlobalStates.hpp> +#include <Cplt/Model/Project.hpp> +#include <Cplt/Model/Workflow/Nodes/DocumentNodes.hpp> +#include <Cplt/Model/Workflow/Nodes/NumericNodes.hpp> +#include <Cplt/Model/Workflow/Nodes/TextNodes.hpp> +#include <Cplt/Model/Workflow/Nodes/UserInputNodes.hpp> +#include <Cplt/Model/Workflow/Workflow.hpp> +#include <Cplt/Utils/I18n.hpp> +#include <Cplt/Utils/Macros.hpp> + +#include <IconsFontAwesome.h> +#include <imgui.h> +#include <imgui_node_editor.h> +#include <imgui_stdlib.h> +#include <memory> +#include <span> +#include <vector> + +namespace ImNodes = ax::NodeEditor; + +namespace CPLT_UNITY_ID { +class WorkflowUI +{ +private: + std::unique_ptr<Workflow> mWorkflow; + + ImNodes::EditorContext* mContext; + + ImNodes::NodeId mContextMenuNodeId = 0; + ImNodes::PinId mContextMenuPinId = 0; + ImNodes::LinkId mContextMenuLinkId = 0; + +public: + WorkflowUI(std::unique_ptr<Workflow> workflow) + : mWorkflow{ std::move(workflow) } + { + mContext = ImNodes::CreateEditor(); + } + + ~WorkflowUI() + { + ImNodes::DestroyEditor(mContext); + } + + void Display() + { + ImNodes::SetCurrentEditor(mContext); + ImNodes::Begin(""); + + // Defer creation of tooltip because within the node editor, cursor positioning is going to be off + const char* tooltipMessage = nullptr; + + for (auto& node : mWorkflow->GetNodes()) { + if (!node) continue; + + ImNodes::BeginNode(node->GetId()); + node->Draw(); + ImNodes::EndNode(); + } + + for (auto& conn : mWorkflow->GetConnections()) { + if (!conn.IsValid()) continue; + + auto srcId = mWorkflow->GetNodes()[conn.SourceNode]->GetOutputPinUniqueId(conn.SourcePin); + auto dstId = mWorkflow->GetNodes()[conn.DestinationNode]->GetInputPinUniqueId(conn.DestinationPin); + ImNodes::Link(conn.GetLinkId(), srcId, dstId); + } + + if (ImNodes::BeginCreate()) { + ImNodes::PinId src = 0, dst = 0; + if (ImNodes::QueryNewLink(&src, &dst)) { + if (!src || !dst) { + goto createError; + } + + auto [srcNode, srcPinId, srcIsOutput] = mWorkflow->DisassembleGlobalPinId(src); + auto [dstNode, dstPinId, dstIsOutput] = mWorkflow->DisassembleGlobalPinId(dst); + + if (srcNode == dstNode) { + ImNodes::RejectNewItem(); + goto createError; + } + + if (srcIsOutput == dstIsOutput) { + ImNodes::RejectNewItem(); + goto createError; + } + + auto srcPin = srcNode->GetOutputPin(srcPinId); + auto dstPin = dstNode->GetOutputPin(dstPinId); + + if (srcPin.MatchingType != dstPin.MatchingType) { + ImNodes::RejectNewItem(); + goto createError; + } + + if (ImNodes::AcceptNewItem()) { + mWorkflow->Connect(*srcNode, srcPinId, *dstNode, dstPinId); + } + } + + ImNodes::PinId newNodePin = 0; + if (ImNodes::QueryNewNode(&newNodePin)) { + auto [node, pinId, isOutput] = mWorkflow->DisassembleGlobalPinId(newNodePin); + + if ((isOutput && node->GetOutputPin(pinId).IsConnected()) || + (!isOutput && node->GetInputPin(pinId).IsConnected())) + { + ImNodes::RejectNewItem(); + goto createError; + } + + if (ImNodes::AcceptNewItem()) { + ImNodes::Suspend(); + ImGui::BeginPopup("CreateNodeCtxMenu"); + ImNodes::Resume(); + } + } + } + createError: + ImNodes::EndCreate(); + + if (ImNodes::BeginDelete()) { + ImNodes::LinkId deletedLinkId; + if (ImNodes::QueryDeletedLink(&deletedLinkId)) { + auto& conn = *mWorkflow->GetConnectionByLinkId(deletedLinkId); + mWorkflow->RemoveConnection(conn.Id); + } + + ImNodes::NodeId deletedNodeId; + if (ImNodes::QueryDeletedNode(&deletedNodeId)) { + auto node = mWorkflow->GetNodeByNodeId(deletedNodeId); + if (!node) { + ImNodes::RejectDeletedItem(); + goto deleteError; + } + + if (node->IsLocked()) { + ImNodes::RejectDeletedItem(); + goto deleteError; + } + } + } + deleteError: + ImNodes::EndDelete(); + + // Popups + ImNodes::Suspend(); + if (ImNodes::ShowNodeContextMenu(&mContextMenuNodeId)) { + ImGui::OpenPopup("NodeCtxMenu"); + } else if (ImNodes::ShowPinContextMenu(&mContextMenuPinId)) { + ImGui::OpenPopup("PinCtxMenu"); + } else if (ImNodes::ShowLinkContextMenu(&mContextMenuLinkId)) { + ImGui::OpenPopup("LinkCtxMenu"); + } + + if (ImGui::BeginPopup("NodeCtxMenu")) { + auto& node = *mWorkflow->GetNodeByNodeId(mContextMenuNodeId); + node.DrawDebugInfo(); + + if (ImGui::MenuItem(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE))) { + ImNodes::DeleteNode(mContextMenuNodeId); + } + + ImGui::EndPopup(); + } + + if (ImGui::BeginPopup("PinCtxMenu")) { + auto [node, pinId, isOutput] = mWorkflow->DisassembleGlobalPinId(mContextMenuPinId); + if (isOutput) { + node->DrawOutputPinDebugInfo(pinId); + } else { + node->DrawInputPinDebugInfo(pinId); + } + + if (ImGui::MenuItem(ICON_FA_UNLINK " " I18N_TEXT("Disconnect", L10N_DISCONNECT))) { + if (isOutput) { + auto& pin = node->GetOutputPin(pinId); + if (pin.IsConnected()) { + auto linkId = mWorkflow->GetConnectionById(pin.Connection)->GetLinkId(); + ImNodes::DeleteLink(linkId); + } + } else { + auto& pin = node->GetInputPin(pinId); + if (pin.IsConstantConnection()) { + // TODO + } else if (pin.IsConnected()) { + auto linkId = mWorkflow->GetConnectionById(pin.Connection)->GetLinkId(); + ImNodes::DeleteLink(linkId); + } + } + } + + ImGui::EndPopup(); + } + + if (ImGui::BeginPopup("LinkCtxMenu")) { + auto& conn = *mWorkflow->GetConnectionByLinkId(mContextMenuLinkId); + conn.DrawDebugInfo(); + + if (ImGui::MenuItem(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE))) { + ImNodes::DeleteLink(mContextMenuLinkId); + } + + ImGui::EndPopup(); + } + + if (ImGui::BeginPopup("CreateNodeCtxMenu")) { + for (int i = WorkflowNode::CG_Numeric; i < WorkflowNode::InvalidCategory; ++i) { + auto category = (WorkflowNode::Category)i; + auto members = WorkflowNode::QueryCategoryMembers(category); + + if (ImGui::BeginMenu(WorkflowNode::FormatCategory(category))) { + for (auto member : members) { + if (ImGui::MenuItem(WorkflowNode::FormatKind(member))) { + // Create node + auto uptr = WorkflowNode::CreateByKind(member); + mWorkflow->AddNode(std::move(uptr)); + } + } + ImGui::EndMenu(); + } + } + ImGui::EndPopup(); + } + + if (tooltipMessage) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(tooltipMessage); + ImGui::EndTooltip(); + } + ImNodes::Resume(); + + ImNodes::End(); + } + + void Close() + { + // TODO + } +}; +} // namespace CPLT_UNITY_ID + +void UI::WorkflowsTab() +{ + auto& project = *GlobalStates::GetInstance().GetCurrentProject(); + + static std::unique_ptr<CPLT_UNITY_ID::WorkflowUI> openWorkflow; + static AssetList::ListState state; + bool openedDummy = true; + + // Toolbar item: close + if (ImGui::Button(ICON_FA_TIMES " " I18N_TEXT("Close", L10N_CLOSE), openWorkflow == nullptr)) { + openWorkflow->Close(); + openWorkflow = nullptr; + } + + // Toolbar item: open... + ImGui::SameLine(); + if (ImGui::Button((I18N_TEXT("Open asset...", L10N_ASSET_OPEN)))) { + ImGui::OpenPopup(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE)); + } + if (ImGui::BeginPopupModal(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::Button(ICON_FA_FOLDER_OPEN " " I18N_TEXT("Open", L10N_OPEN), state.SelectedAsset == nullptr)) { + ImGui::CloseCurrentPopup(); + + auto workflow = project.Workflows.Load(*state.SelectedAsset); + openWorkflow = std::make_unique<CPLT_UNITY_ID::WorkflowUI>(std::move(workflow)); + } + ImGui::SameLine(); + project.Workflows.DisplayControls(state); + project.Workflows.DisplayDetailsList(state); + + ImGui::EndPopup(); + } + + // Toolbar item: manage... + ImGui::SameLine(); + if (ImGui::Button(I18N_TEXT("Manage assets...", L10N_ASSET_MANAGE))) { + ImGui::OpenPopup(I18N_TEXT("Manage assets", L10N_ASSET_MANAGE_DIALOG_TITLE)); + } + if (ImGui::BeginPopupModal(I18N_TEXT("Manage assets", L10N_ASSET_MANAGE_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { + project.Workflows.DisplayControls(state); + project.Workflows.DisplayDetailsList(state); + ImGui::EndPopup(); + } + + if (openWorkflow) { + openWorkflow->Display(); + } +} |