#include "UI_Layout.hpp" #include "imgui_impl_win32.h" #include "Global_Variables.hpp" #include "Config.hpp" #include "DLT_Utilities.hpp" extern "C" { #include "windows.h" #include "stdio.h" } #include "thread" // UI布局 void UI_Layout() { // 配置窗口绘制位置为Dear ImGUI生成的主窗口 const ImGuiViewport *viewport = ImGui::GetMainViewport(); // 下一个窗体绘制到主窗体位置 ImGui::SetNextWindowPos(viewport->WorkPos); // 下一个窗体绘制为主窗体大小 ImGui::SetNextWindowSize(viewport->WorkSize); // 设置下一个焦点窗体为主窗体 ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); // 绘制风格栈压入窗口圆角为0 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); // 绘制风格栈压入窗体边框宽度为0 // 窗体选项配置 ImGuiWindowFlags window_flags = ImGuiWindowFlags_None; // 设置窗体相关属性,无标题栏,无折叠标志,不可重塑大小,不可移动 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; // 设置窗体相关属性,收到焦点时不移动到前台,禁用焦点事件 window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; // 设置窗体相关属性,不允许停靠 window_flags |= ImGuiWindowFlags_NoDocking; ImGui::Begin("Main Panel", NULL, window_flags); // 创建主面板 ImGui::PopStyleVar(2); // 退出绘制风格栈中的设置项 { const ImGuiStyle &style = ImGui::GetStyle(); float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY)); // 拖拽输入区域 Widgets::Drag_Input_Area(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x / 2 > MAIN_FRAME_SIZE_WIDTH_MIN / 2 ? MAIN_FRAME_SIZE_WIDTH_MIN / 2 : ImGui::GetContentRegionAvail().x / 2)); // 文件列表组件 Widgets::File_List_Area(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - (ImGui::GetTextLineHeightWithSpacing() + 20 * main_scale + style.ItemSpacing.y))); // 导出文件 Widgets::Control_Panel(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() + 20 * main_scale)); } ImGui::End(); // ImGui::ShowDemoWindow(nullptr); } void Widgets::Drag_Input_Area(const ImVec2 &size) { ImGui::BeginChild("Drag_Drop_Area", size); { ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f); // 绘制风格栈压入窗口圆角为0 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); // 显示一个按钮,作为拖拽源 static bool pressed_hover = false; if (ImGui::Button(pressed_hover ? u8"松开左键以添加" : u8"单击选择文件或拖拽文件到此处", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y))) { OPENFILENAMEW ofn; // common dialog box structure wchar_t szFile[MAX_PATH * MAX_SINGLE_SELECT_FILE_COUNT] = {L'\0'}; // buffer for file name // Initialize OPENFILENAME ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); // 结构体大小 ofn.hwndOwner = Main_Window_hWnd; // 对话框唤起窗体 ofn.lpstrFile = szFile; // 输出缓冲区 ofn.nMaxFile = sizeof(szFile); // 缓冲区大小 ofn.lpstrFilter = L"(.dlt)\0*.dlt\0"; // 文件格式筛选器 ofn.nFilterIndex = 1; // 默认选择的筛选器 // 文件路径必须存在 | 文件本身必须存在 | 允许多文件选择 | 使用资源管理器 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; // Display the Open dialog box. // 处理文件输入 if (GetOpenFileNameW(&ofn)) { // 重复输入检测 bool with_duplicated_input = false; wchar_t *p_first_str = ofn.lpstrFile; // 单文件 if ((p_first_str[wcslen(p_first_str)] == L'\0') && (p_first_str[wcslen(p_first_str) + 1] == L'\0')) { char file_path_multi_byte[MAX_PATH] = {'\0'}; int size_needed = WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, NULL, 0, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, file_path_multi_byte, size_needed, NULL, NULL); for (Input_File_Node *p_file_node = input_dlt_file_list_head; p_file_node != nullptr; p_file_node = p_file_node->p_next) if (!strcmp(p_file_node->file_name_str, file_path_multi_byte)) { with_duplicated_input = true; break; } if (with_duplicated_input) { MessageBoxW(Main_Window_hWnd, L"存在重复输入", L"", MB_OK | MB_ICONWARNING); } else { // 开辟新文件节点 Input_File_Node *p_new_file_node = (Input_File_Node *)malloc(sizeof(Input_File_Node)); p_new_file_node->file_name_str = (char *)malloc(strlen(file_path_multi_byte) + 1); memset(p_new_file_node->file_name_str, '\0', strlen(file_path_multi_byte) + 1); memcpy(p_new_file_node->file_name_str, file_path_multi_byte, strlen(file_path_multi_byte) + 1); p_new_file_node->p_next = nullptr; // 插入文件列表 if (input_dlt_file_list_head == nullptr) input_dlt_file_list_head = p_new_file_node; else { Input_File_Node *p_target_node = input_dlt_file_list_head; for (size_t count = 1; count < input_dlt_file_count; count++) p_target_node = p_target_node->p_next; p_target_node->p_next = p_new_file_node; } input_dlt_file_count++; } } else { // 多文件输入时 char public_path_multi_byte[MAX_PATH] = {'\0'}; int size_needed = WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, NULL, 0, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, public_path_multi_byte, size_needed, NULL, NULL); for (wchar_t *p_file_name_str = ofn.lpstrFile + wcslen(ofn.lpstrFile) + 1; *p_file_name_str != L'\0'; p_file_name_str += (wcslen(p_file_name_str) + 1)) { char file_name_multi_byte[MAX_PATH] = {'\0'}; char file_path_multi_byte[MAX_PATH] = {'\0'}; int size_needed = WideCharToMultiByte(CP_UTF8, 0, p_file_name_str, -1, NULL, 0, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, p_file_name_str, -1, file_name_multi_byte, size_needed, NULL, NULL); // 拼接完整路径 sprintf(file_path_multi_byte, "%s\\%s", public_path_multi_byte, file_name_multi_byte); for (Input_File_Node *p_file_node = input_dlt_file_list_head; p_file_node != nullptr; p_file_node = p_file_node->p_next) if (!strcmp(p_file_node->file_name_str, file_path_multi_byte)) { with_duplicated_input = true; break; } if (with_duplicated_input) continue; // 开辟新文件节点 Input_File_Node *p_new_file_node = (Input_File_Node *)malloc(sizeof(Input_File_Node)); p_new_file_node->file_name_str = (char *)malloc(strlen(file_path_multi_byte) + 1); memset(p_new_file_node->file_name_str, '\0', strlen(file_path_multi_byte) + 1); memcpy(p_new_file_node->file_name_str, file_path_multi_byte, strlen(file_path_multi_byte) + 1); p_new_file_node->p_next = nullptr; // 插入文件列表 if (input_dlt_file_list_head == nullptr) input_dlt_file_list_head = p_new_file_node; else { Input_File_Node *p_target_node = input_dlt_file_list_head; for (size_t count = 1; count < input_dlt_file_count; count++) p_target_node = p_target_node->p_next; p_target_node->p_next = p_new_file_node; } input_dlt_file_count++; } if (with_duplicated_input) { MessageBoxW(Main_Window_hWnd, L"存在重复输入", L"", MB_OK); } } } } // 仅在外部拖拽入内时触发,内部拖拽不触发 pressed_hover = (ImGui::IsItemHovered() && (GetAsyncKeyState(VK_LBUTTON) < 0) && (!ImGui::IsMouseDown(ImGuiMouseButton_Left))); ImGui::PopStyleVar(2); } ImGui::EndChild(); } void Widgets::File_List_Area(const ImVec2 &size) { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f); ImGui::BeginChild("File_List_Area_Wrapper", size, ImGuiChildFlags_Border); ImGui::PopStyleVar(); { int element_id = 0; Input_File_Node *p_file_node_pre = nullptr; Input_File_Node *p_file_node = input_dlt_file_list_head; for (size_t count = 0; count < input_dlt_file_count; count++) { ImGui::PushID(element_id++); // 点击了删除 if (ImGui::Button("X")) { if (p_file_node == input_dlt_file_list_head) { free(input_dlt_file_list_head->file_name_str); input_dlt_file_list_head = input_dlt_file_list_head->p_next; free(p_file_node); p_file_node = input_dlt_file_list_head; } else { p_file_node_pre->p_next = p_file_node->p_next; free(p_file_node->file_name_str); free(p_file_node); } input_dlt_file_count--; count--; ImGui::PopID(); continue; } ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::InputText("##Text_Show", p_file_node->file_name_str, strlen(p_file_node->file_name_str) + 1, ImGuiInputTextFlags_ElideLeft | ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoUndoRedo); ImGui::PopID(); p_file_node = p_file_node->p_next; if (count == 0) p_file_node_pre = input_dlt_file_list_head; else p_file_node_pre = p_file_node_pre->p_next; } } ImGui::EndChild(); } void Widgets::Workflow_Area(const ImVec2 &size) { ImNodes::BeginNodeEditor(); { ImNodes::BeginNode(1); ImNodes::BeginNodeTitleBar(); 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::EndNodeEditor(); } void Widgets::Control_Panel(const ImVec2 &size) { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::BeginChild("Control_Panel", size, ImGuiChildFlags_Border); ImGui::PopStyleVar(2); { static bool export_confirmed = false; static bool export_task_created = false; static char export_file_path_str[MAX_PATH] = {'\0'}; if (!export_confirmed) { ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f); if (ImGui::Button(u8"导出", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y))) { if (input_dlt_file_list_head == nullptr) MessageBoxW(Main_Window_hWnd, L"需要至少输入一个文件", L"", MB_OK | MB_ICONERROR); else { OPENFILENAMEW ofn; // common dialog box structure static wchar_t szFile[MAX_PATH] = {L'\0'}; // buffer for file name // Initialize OPENFILENAME ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); // 结构体大小 ofn.hwndOwner = Main_Window_hWnd; // 对话框唤起窗体 ofn.lpstrFile = szFile; // 输出缓冲区 ofn.nMaxFile = sizeof(szFile); // 缓冲区大小 ofn.lpstrFilter = L"(.csv)\0*.csv\0"; // 文件格式筛选器 ofn.nFilterIndex = 1; // 默认选择的筛选器 // 文件路径必须存在 | 文件本身必须存在 | 使用资源管理器 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; if (GetSaveFileNameW(&ofn)) { // 提取文件路径 memset(export_file_path_str, '\0', sizeof(export_file_path_str)); int size_needed = WideCharToMultiByte(CP_UTF8, 0, szFile, -1, NULL, 0, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, szFile, -1, export_file_path_str, size_needed, NULL, NULL); wchar_t File_Name[MAX_PATH] = {L'\0'}; wchar_t *p_str_end = szFile + wcslen(szFile); // 处理后缀 char *p_extension_name = export_file_path_str + (strlen(export_file_path_str) - 4); if (p_extension_name[0] != '.' || toupper(p_extension_name[1]) != 'C' || toupper(p_extension_name[2]) != 'S' || toupper(p_extension_name[3]) != 'V') { p_extension_name += 4; p_extension_name[0] = '.'; p_extension_name[1] = 'c'; p_extension_name[2] = 's'; p_extension_name[3] = 'v'; p_str_end[0] = L'.'; p_str_end[1] = L'c'; p_str_end[2] = L's'; p_str_end[3] = L'v'; } while (p_str_end != szFile && *(p_str_end - 1) != L'\\') p_str_end--; size_t len = wcslen(p_str_end); for (size_t count = 0; count < len; count++, p_str_end++) File_Name[count] = *p_str_end; memcpy(szFile, File_Name, sizeof(File_Name)); // 判定是否覆盖文件 FILE *p_file = fopen(export_file_path_str, "rb"); if (p_file != nullptr) { fclose(p_file); if (MessageBoxW(Main_Window_hWnd, L"文件已存在,是否覆盖?", L"", MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING) == IDYES) export_confirmed = true; } else export_confirmed = true; } } } ImGui::PopStyleVar(); } else { // 创建导出任务 if (!export_task_created) { export_task_created = true; // 执行导出任务的线程 std::thread export_thread( [&]() { DLT_Log Log; Input_File_Node *p_file_list = input_dlt_file_list_head; DLT_Type::DLT_Err err; while (p_file_list != nullptr) { err = Log.load_from_file(p_file_list->file_name_str); if (err != DLT_Type::DLT_ERROR_NONE) break; p_file_list = p_file_list->p_next; } if (err != DLT_Type::DLT_ERROR_NONE) { switch (err) { case DLT_Type::DLT_ERROR_FILE_OPEN_FAILED: // 文件打开失败 MessageBoxW(Main_Window_hWnd, L"文件打开失败,检查路径是否存在特殊字符", L"", MB_OK | MB_ICONERROR); break; default: MessageBoxW(Main_Window_hWnd, L"文件加载失败", L"", MB_OK | MB_ICONERROR); } } else { // 排序后导出 Log.sort_msg_list(); err = Log.export_to_csv(export_file_path_str); if (err == DLT_Type::DLT_ERROR_NONE) MessageBoxW(Main_Window_hWnd, L"导出完成", L"", MB_OK); else MessageBoxW(Main_Window_hWnd, L"导出失败,检查文件占用", L"", MB_OK | MB_ICONERROR); } export_confirmed = false; export_task_created = false; }); // 分离线程 export_thread.detach(); } ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 12.5f); ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), u8"正在导出..."); ImGui::PopStyleVar(); } } ImGui::EndChild(); }