#include "Workflow-Editor.hpp" #include "imgui.h" #include "imgui_internal.h" #include "imnodes.h" #include // 构造函数 Workflow_Editor_Class::Workflow_Editor_Class() {} // 析构函数 Workflow_Editor_Class::~Workflow_Editor_Class() { // 释放资源 for (auto &pNode : this->Node_List) delete pNode; } // 客制化圆形带X按钮 bool CircleButtonWithX(const char *id, float radius) { ImGuiWindow *window = ImGui::GetCurrentWindow(); if (window->SkipItems) return false; // 创建ID ImGuiID button_id = window->GetID(id); // 计算按钮位置和大小 ImVec2 pos = window->DC.CursorPos; ImVec2 size(radius * 2, radius * 2); ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); // 处理交互 ImGui::ItemSize(bb); if (!ImGui::ItemAdd(bb, button_id)) return false; bool hovered, held; bool pressed = ImGui::ButtonBehavior(bb, button_id, &hovered, &held); // 绘制 ImU32 col = ImGui::GetColorU32( held ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); // 获取绘制列表 ImDrawList *draw_list = ImGui::GetWindowDrawList(); ImVec2 center = ImVec2(pos.x + radius, pos.y + radius); // 绘制圆形 draw_list->AddCircleFilled(center, radius, col, 32); // 绘制边框(可选) draw_list->AddCircle(center, radius, ImGui::GetColorU32(ImGuiCol_Border), 32, 1.0f); // 计算X的线条 float cross_radius = radius * 0.5f; // X的大小 float thickness = radius * 0.15f; // X的粗细 // 绘制X的两条线 draw_list->AddLine( ImVec2(center.x - cross_radius, center.y - cross_radius), ImVec2(center.x + cross_radius, center.y + cross_radius), ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)), // 白色X thickness); draw_list->AddLine( ImVec2(center.x + cross_radius, center.y - cross_radius), ImVec2(center.x - cross_radius, center.y + cross_radius), ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)), // 白色X thickness); return pressed; } // 消息行输入节点绘制函数 bool MsgLine_Input_Node_Class::show_and_get_state() { // 节点宽度 const float node_width = 200.0f; bool delete_flag = false; // 初始化位置设定 if (this->initial_state) { ImNodes::SetNodeScreenSpacePos(this->id, this->initial_position); this->initial_state = false; } // 绘制节点 ImNodes::BeginNode(this->id); { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ImGui::GetStyle().ItemSpacing.y)); // 标题部分 ImNodes::BeginNodeTitleBar(); { ImGui::TextUnformatted(u8"DLT-信息输入节点"); ImGui::SameLine(); ImGui::Dummy(ImVec2(node_width - ImGui::CalcTextSize(u8"DLT-信息输入节点").x - ImGui::GetTextLineHeight(), 0.0f)); ImGui::SameLine(); // 绘制关闭按钮 if (CircleButtonWithX("#X", ImGui::GetTextLineHeight() / 2)) delete_flag = true; } ImNodes::EndNodeTitleBar(); // // 正文部分 // ImGui::Text(u8"DLT消息输入流\n每次输出一条消息"); // ImGui::Text(u8"(节点ID : %d)", this->id); // 链接点 ImNodes::BeginOutputAttribute(0, 1); // const float label_width = ImGui::CalcTextSize(u8"DLT消息行->").x; // ImGui::Indent(label_width); ImGui::Dummy(ImVec2(node_width - ImGui::CalcTextSize("(?) DLT MSG ->").x, 0.0f)); ImGui::SameLine(); // Help Mark { ImGui::TextDisabled("(?) "); if (ImGui::BeginItemTooltip()) { ImGui::TextUnformatted(u8"每个处理流程开始时,输出一条DLT消息;\n每个此类节点输出的消息均会独立遍历所有输入文件;"); ImGui::EndTooltip(); } } ImGui::SameLine(); ImGui::TextUnformatted("DLT MSG ->"); ImNodes::EndOutputAttribute(); ImGui::PopStyleVar(); } ImNodes::EndNode(); // 返回节点状态 return delete_flag; } // 显示工作流节点编辑器 void Workflow_Editor_Class::Show(void) { // 浅色主题 ImNodes::StyleColorsLight(); ImNodes::BeginNodeEditor(); ImGui::PopStyleVar(); { // 显示所有节点 for (auto iterator = this->Node_List.begin(); iterator != this->Node_List.end(); iterator++) { if ((*iterator)->show_and_get_state()) { delete *iterator; // 迭代器删除后会返回下一个位置的迭代器 iterator = this->Node_List.erase(iterator); iterator--; } } // 右键菜单 { const bool open_popup = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImNodes::IsEditorHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Right); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); if (!ImGui::IsAnyItemHovered() && open_popup) { ImGui::OpenPopup("RightClick_Popup"); } if (ImGui::BeginPopup("RightClick_Popup")) { ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup(); // 节点添加菜单 if (ImGui::BeginMenu("Add Nodes")) { if (ImGui::MenuItem("Msg input node.")) this->Add_Node(NODE_TYPE_MSG_LINE_INPUT, click_pos); ImGui::EndMenu(); } ImGui::EndPopup(); } } { // ImNodes::BeginNode(1); // ImNodes::BeginNodeTitleBar(); // // ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f); // 绘制风格栈压入窗口圆角为0 // // ImGui::Button("X"); // // ImGui::PopStyleVar(); // if (CircleButtonWithX("#X", ImGui::GetTextLineHeight() / 2)) // { // }; // ImGui::SameLine(); // ImGui::TextUnformatted("Output Node"); // ImNodes::EndNodeTitleBar(); // ImGui::Text("Test Format %%d :%d", 123); // ImGui::Button("Click"); // ImGui::SameLine(); // static bool check = false; // ImGui::Checkbox("", &check); // ImNodes::EndNode(); } } ImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_TopRight); ImNodes::EndNodeEditor(); } // 添加指定类型节点 void Workflow_Editor_Class::Add_Node(NodeType type, ImVec2 initial_position) { // 获取可用ID auto get_id = [&]() -> int { int target_id = 0; for (auto &pNode : this->Node_List) if (target_id == pNode->getID()) target_id++; else break; return target_id; }; // 记录节点 switch (type) { case NODE_TYPE_MSG_LINE_INPUT: // 消息行输入 this->Node_List.push_back(new MsgLine_Input_Node_Class(get_id(), initial_position)); break; case NODE_TYPE_CSV_EXPORTER: // CSV输出器 default: break; } // 重新排序确保后续ID分配正常 std::sort(this->Node_List.begin(), this->Node_List.end(), [](Node_Class *a, Node_Class *b) -> bool { return a->getID() < b->getID(); }); }