aboutsummaryrefslogtreecommitdiff
path: root/app/source/Cplt/UI/UI_Workflows.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'app/source/Cplt/UI/UI_Workflows.cpp')
-rw-r--r--app/source/Cplt/UI/UI_Workflows.cpp293
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();
+ }
+}