diff --git a/Inc/Config.hpp b/Inc/Config.hpp index 3555fe6..6dbc958 100644 --- a/Inc/Config.hpp +++ b/Inc/Config.hpp @@ -5,7 +5,10 @@ #define MAIN_FRAME_TITTLE L"DLT Splitter —— Dev by : LuChiChick" // 主窗体最小面积 -#define MAIN_FRAME_SIZE_WIDTH_MIN 350 -#define MAIN_FRAME_SIZE_HEIGHT_MIN 350 * 1.5 +#define MAIN_FRAME_SIZE_WIDTH_MIN 400 +#define MAIN_FRAME_SIZE_HEIGHT_MIN 400 * 1.5 + +// 单次允许选择的最大文件数量 +#define MAX_SINGLE_SELECT_FILE_COUNT 512 #endif \ No newline at end of file diff --git a/Inc/Global_Variables.hpp b/Inc/Global_Variables.hpp index 975d900..33e7dbc 100644 --- a/Inc/Global_Variables.hpp +++ b/Inc/Global_Variables.hpp @@ -13,6 +13,9 @@ extern "C" #include "Type_Descriptions.hpp" +// 主窗体句柄 +extern HWND Main_Window_hWnd; + // D3D11 相关 extern ID3D11Device *g_pd3dDevice; // D3D11 设备句柄 extern ID3D11DeviceContext *g_pd3dDeviceContext; // D3D11 设备上下文句柄 diff --git a/Src/Global_Variables.cpp b/Src/Global_Variables.cpp index 4027b43..03c1577 100644 --- a/Src/Global_Variables.cpp +++ b/Src/Global_Variables.cpp @@ -1,5 +1,8 @@ #include "Global_Variables.hpp" +// 主窗体句柄 +HWND Main_Window_hWnd = nullptr; + // D3D11 相关 ID3D11Device *g_pd3dDevice = nullptr; // D3D11 设备句柄 ID3D11DeviceContext *g_pd3dDeviceContext = nullptr; // D3D11 设备上下文句柄 diff --git a/Src/Main.cpp b/Src/Main.cpp index f2d10ce..90379d9 100644 --- a/Src/Main.cpp +++ b/Src/Main.cpp @@ -61,20 +61,20 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine RegisterClassExW(&wc); // 创建窗体 - HWND hwnd = CreateWindowW(wc.lpszClassName, // 创建的窗体类名 - MAIN_FRAME_TITTLE, // 窗体标题 - WS_OVERLAPPEDWINDOW, // 窗体风格为标准可重叠的顶层窗口 - 100, // 起始坐标x - 100, // 起始坐标y - (int)(MAIN_FRAME_SIZE_WIDTH_MIN * main_scale), // 窗体长 - (int)(MAIN_FRAME_SIZE_HEIGHT_MIN * main_scale), // 窗体宽 - nullptr, // 父窗体句柄 - nullptr, // 菜单句柄 - wc.hInstance, // 创建实例句柄 - nullptr); // 额外的事件参数指针 + Main_Window_hWnd = CreateWindowW(wc.lpszClassName, // 创建的窗体类名 + MAIN_FRAME_TITTLE, // 窗体标题 + WS_OVERLAPPEDWINDOW, // 窗体风格为标准可重叠的顶层窗口 + 100, // 起始坐标x + 100, // 起始坐标y + (int)(MAIN_FRAME_SIZE_WIDTH_MIN * main_scale), // 窗体长 + (int)(MAIN_FRAME_SIZE_HEIGHT_MIN * main_scale), // 窗体宽 + nullptr, // 父窗体句柄 + nullptr, // 菜单句柄 + wc.hInstance, // 创建实例句柄 + nullptr); // 额外的事件参数指针 // 在窗体内创建 Direct 3D 设备 - if (!CreateDeviceD3D(hwnd)) + if (!CreateDeviceD3D(Main_Window_hWnd)) { // 未创建完成时清理 Direct 3D 设备并清理窗体注册类 CleanupDeviceD3D(); @@ -85,11 +85,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } // 启用文件拖拽输入 - DragAcceptFiles(hwnd, TRUE); + DragAcceptFiles(Main_Window_hWnd, TRUE); // 显示窗体并强制更新一次窗体 - ShowWindow(hwnd, SW_SHOWDEFAULT); - UpdateWindow(hwnd); + ShowWindow(Main_Window_hWnd, SW_SHOWDEFAULT); + UpdateWindow(Main_Window_hWnd); // 检查版本 IMGUI_CHECKVERSION(); @@ -133,7 +133,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } // 初始化渲染器后端(Renderer backends) - ImGui_ImplWin32_Init(hwnd); + ImGui_ImplWin32_Init(Main_Window_hWnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // 字体配置相关 @@ -164,7 +164,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { // 非阻塞式获取所有窗体消息,Pick消息后从消息队列删除,并转译和派发消息 MSG msg; - while (PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) + while (PeekMessageW(&msg, nullptr, 0U, 0U, PM_REMOVE)) { TranslateMessage(&msg); // 转译 DispatchMessageW(&msg); // 派发 @@ -236,7 +236,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine ImNodes::DestroyContext(); // 销毁 ImNodes 上下文 CleanupDeviceD3D(); // 清理 Direct 3D 设备 - DestroyWindow(hwnd); // 销毁窗体 + DestroyWindow(Main_Window_hWnd); // 销毁窗体 UnregisterClassW(wc.lpszClassName, wc.hInstance); // 取消窗体注册类 return 0; @@ -342,6 +342,10 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) // 获取拖拽的文件总数 UINT fileCount = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0); + // 非法输入检测 + bool with_illegal_input = false; + bool with_duplicated_input = false; + // 添加到文件列表 for (UINT count = 0; count < fileCount; count++) { @@ -357,26 +361,31 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) int size_needed = WideCharToMultiByte(CP_UTF8, 0, filePath, -1, NULL, 0, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, filePath, -1, file_path_multi_byte, size_needed, NULL, NULL); - /** - * @todo 文件类型筛选 - */ - // 重复文件剔除 bool duplicated = false; 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)) { duplicated = true; + with_duplicated_input = true; break; } if (duplicated) continue; + // 文件类型后缀过滤 + const char *p_extension_name = file_path_multi_byte + (strlen(file_path_multi_byte) - 4); + if (p_extension_name[0] != '.' || toupper(p_extension_name[1]) != 'D' || toupper(p_extension_name[2]) != 'L' || toupper(p_extension_name[3]) != 'T') + { + with_illegal_input = true; + 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)); + 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; // 插入文件列表 @@ -396,6 +405,13 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) // 释放拖拽资源(必须调用,否则内存泄漏) DragFinish(hDrop); + + if (with_duplicated_input) + MessageBoxW(hWnd, L"存在重复输入", L"", MB_OK); + + if (with_illegal_input) + MessageBoxW(hWnd, L"存在非法输入,仅支持 .dlt 文件", L"", MB_OK); + return 0; } case WM_SIZE: // 窗体大小变事件 diff --git a/Src/UI_Layout.cpp b/Src/UI_Layout.cpp index 42f1b26..3d50370 100644 --- a/Src/UI_Layout.cpp +++ b/Src/UI_Layout.cpp @@ -1,7 +1,12 @@ #include "UI_Layout.hpp" +#include "imgui_impl_win32.h" +#include "Global_Variables.hpp" +#include "Config.hpp" + extern "C" { #include "windows.h" +#include "stdio.h" } // UI布局 @@ -51,7 +56,133 @@ void Widgets::Drag_Input_Area(const ImVec2 &size) ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); // 显示一个按钮,作为拖拽源 static bool pressed_hover = false; - ImGui::Button(pressed_hover ? u8"松开左键以添加" : u8"拖拽文件到此处", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y)); + + 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; + // Set lpstrFile[0] to '\0' so that GetOpenFileName does not + // use the contents of szFile to initialize itself. + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = L"(.dlt)\0*.dlt\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; + + // Display the Open dialog box. + + // 处理文件输入 + if (GetOpenFileNameW(&ofn) == TRUE) + { + // 重复输入检测 + 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); + } + 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))); @@ -63,22 +194,52 @@ void Widgets::Drag_Input_Area(const ImVec2 &size) void Widgets::File_List_Area(const ImVec2 &size) { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f); - ImGui::BeginChild("File_List_Area", ImVec2(size.x, size.y), ImGuiChildFlags_Border); + ImGui::BeginChild("File_List_Area_Wrapper", ImVec2(size.x, size.y), ImGuiChildFlags_Border); ImGui::PopStyleVar(); { - ImGui::Dummy(ImVec2(ImGui::GetContentRegionAvail().x - 30, ImGui::GetContentRegionAvail().y)); + 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++); - const float spacing = 4; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f); // 绘制风格栈压入窗口圆角为10 - ImGui::PushStyleVar(ImGuiStyleVar_GrabRounding, 10.0f); // 抓取条圆角 - static int int_value = 0; - ImGui::SameLine(); + // 点击了删除 + 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::VSliderInt("int", ImVec2(30, ImGui::GetContentRegionAvail().y), &int_value, 0, 5); + ImGui::SameLine(); - ImGui::PopStyleVar(3); + 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(); }