diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index dc7e66f02e24..b52197155d03 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -799,6 +799,51 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA bd->WantUpdateHasGamepad = true; #endif return 0; + + case WM_DROPFILES: + { + // Process dropped files + HDROP hDrop = (HDROP)wParam; + UINT fileCount = ::DragQueryFile(hDrop, 0xFFFFFFFF, nullptr, 0); + + // Start drag and drop operation with external source flag + ImGuiContext* ctx = ImGui::GetCurrentContext(); + if (ctx != nullptr) + { + ImGuiPayload& payload = ctx->DragDropPayload; + if (!payload.SourceId) + { + // Begin drag drop source with external flag + ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceExtern); + + // Add payload for each dropped file + for (UINT i = 0; i < fileCount; i++) + { + // Get file path + wchar_t filePath[MAX_PATH]; + ::DragQueryFile(hDrop, i, filePath, MAX_PATH); + + // Convert to UTF-8 + int utf8Length = ::WideCharToMultiByte(CP_UTF8, 0, filePath, -1, nullptr, 0, nullptr, nullptr); + if (utf8Length > 0) + { + std::vector utf8FilePath(utf8Length); + ::WideCharToMultiByte(CP_UTF8, 0, filePath, -1, utf8FilePath.data(), utf8Length, nullptr, nullptr); + + // Add payload item with file path + ImGui::AddDragDropPayload("DND_FILE_PATH", utf8FilePath.data(), utf8FilePath.size() - 1); + } + } + + // End drag drop source + ImGui::EndDragDropSource(); + } + } + + // Release drop handle + ::DragFinish(hDrop); + return 0; + } } return 0; } @@ -962,6 +1007,12 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) } } +void ImGui_ImplWin32_EnableDragDropAcceptFiles(void* hwnd, bool enabled) +{ + IM_ASSERT(hwnd != NULL); + ::DragAcceptFiles((HWND)hwnd, enabled ? TRUE : FALSE); +} + //--------------------------------------------------------------------------------------------------------- #if defined(__GNUC__) diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 5ae399e0e24b..f0b1be1a4da5 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -49,5 +49,6 @@ IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // // - Use to enable alpha compositing transparency with the desktop. // - Use together with e.g. clearing your framebuffer with zero-alpha. IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd +IMGUI_IMPL_API void ImGui_ImplWin32_EnableDragDropAcceptFiles(void* hwnd, bool enabled); // HWND hwnd #endif // #ifndef IMGUI_DISABLE diff --git a/imgui.cpp b/imgui.cpp index 7ad8ee89da9f..52b74e4305db 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14697,6 +14697,44 @@ void ImGui::EndDragDropSource() } // Use 'cond' to choose to submit payload on drag start or every frame +bool ImGui::AddDragDropPayload(const char* type, const void* data, size_t data_size) +{ + ImGuiContext& g = *GImGui; + ImGuiPayload& payload = g.DragDropPayload; + + IM_ASSERT(type != NULL); + IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(ImGuiPayloadItem::DataType) && "Payload type can be at most 32 characters long"); + IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); + IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() + IM_ASSERT(payload.DataFrameCount != -1); // SetDragDropPayload() must be called first + + // Create new payload item + ImGuiPayloadItem item; + ImStrncpy(item.DataType, type, IM_ARRAYSIZE(item.DataType)); + + // Copy data + if (data_size > 0) + { + // Store in heap + void* item_data = g.DragDropPayloadBufHeap.Data + g.DragDropPayloadBufHeap.Size; + g.DragDropPayloadBufHeap.resize(g.DragDropPayloadBufHeap.Size + (int)data_size); + memcpy(item_data, data, data_size); + item.Data = item_data; + item.DataSize = (int)data_size; + } + else + { + item.Data = NULL; + item.DataSize = 0; + } + + // Add item to payload + payload.Items.push_back(item); + + // Return whether the payload has been accepted + return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); +} + bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) { ImGuiContext& g = *GImGui; @@ -14705,35 +14743,39 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s cond = ImGuiCond_Always; IM_ASSERT(type != NULL); - IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); + IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(ImGuiPayloadItem::DataType) && "Payload type can be at most 32 characters long"); IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) { - // Copy payload - ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); + // Clear existing payload items + payload.Items.clear(); g.DragDropPayloadBufHeap.resize(0); - if (data_size > sizeof(g.DragDropPayloadBufLocal)) + + // Create first payload item + ImGuiPayloadItem item; + ImStrncpy(item.DataType, type, IM_ARRAYSIZE(item.DataType)); + + // Copy data + if (data_size > 0) { // Store in heap g.DragDropPayloadBufHeap.resize((int)data_size); - payload.Data = g.DragDropPayloadBufHeap.Data; - memcpy(payload.Data, data, data_size); - } - else if (data_size > 0) - { - // Store locally - memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); - payload.Data = g.DragDropPayloadBufLocal; - memcpy(payload.Data, data, data_size); + item.Data = g.DragDropPayloadBufHeap.Data; + memcpy(item.Data, data, data_size); + item.DataSize = (int)data_size; } else { - payload.Data = NULL; + item.Data = NULL; + item.DataSize = 0; } - payload.DataSize = (int)data_size; + + // Add item to payload and set as current for backward compatibility + payload.Items.push_back(item); + payload.CurrentItem = &payload.Items[0]; } payload.DataFrameCount = g.FrameCount; @@ -14868,6 +14910,13 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop RenderDragDropTargetRectForItem(r); } + // Set current item for backward compatibility + payload.CurrentItem = NULL; + if (type != NULL) + payload.CurrentItem = payload.GetItemByType(type); + else if (!payload.Items.empty()) + payload.CurrentItem = &payload.Items[0]; + g.DragDropAcceptFrameCount = g.FrameCount; if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1) payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount); diff --git a/imgui.h b/imgui.h index 3e44b9f400bd..280155dc0ba7 100644 --- a/imgui.h +++ b/imgui.h @@ -967,11 +967,13 @@ namespace ImGui // - An item can be both drag source and drop target. IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource() IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted. + IMGUI_API bool AddDragDropPayload(const char* type, const void* data, size_t sz); // Add another payload item to the current drag and drop operation. Return true when payload has been accepted. IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. returns NULL when drag and drop is finished or inactive. use ImGuiPayload::IsDataType() to test for the payload type. + IMGUI_API bool AddDragDropPayload(const char* type, const void* data, size_t sz); // Add another payload item to the current drag and drop operation. Return true when payload has been accepted. // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) @@ -2654,26 +2656,42 @@ struct ImGuiSizeCallbackData ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. }; +// Data payload item for Drag and Drop operations +struct ImGuiPayloadItem +{ + void* Data; // Data (copied and owned by dear imgui) + int DataSize; // Data size + char DataType[32 + 1]; // Data type tag (short user-supplied string, 32 characters max) + + ImGuiPayloadItem() { Clear(); } + void Clear() { Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); } + bool IsDataType(const char* type) const { return strcmp(type, DataType) == 0; } +}; + // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() struct ImGuiPayload { // Members - void* Data; // Data (copied and owned by dear imgui) - int DataSize; // Data size + ImVector Items; // Multiple payload items + const ImGuiPayloadItem* CurrentItem; // Current item for backward compatibility // [Internal] ImGuiID SourceId; // Source item id ImGuiID SourceParentId; // Source parent id (if available) int DataFrameCount; // Data timestamp - char DataType[32 + 1]; // Data type tag (short user-supplied string, 32 characters max) bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets) bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item. ImGuiPayload() { Clear(); } - void Clear() { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; } - bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; } + void Clear() { SourceId = SourceParentId = 0; Items.clear(); CurrentItem = NULL; DataFrameCount = -1; Preview = Delivery = false; } + bool IsDataType(const char* type) const { for (int i = 0; i < Items.Size; i++) if (Items[i].IsDataType(type)) return true; return false; } + const ImGuiPayloadItem* GetItemByType(const char* type) const { for (int i = 0; i < Items.Size; i++) if (Items[i].IsDataType(type)) return &Items[i]; return NULL; } bool IsPreview() const { return Preview; } bool IsDelivery() const { return Delivery; } + // Backward compatibility + const void* GetData() const { return CurrentItem ? CurrentItem->Data : NULL; } + int GetDataSize() const { return CurrentItem ? CurrentItem->DataSize : 0; } + const char* GetDataType() const { return CurrentItem ? CurrentItem->DataType : NULL; } }; //-----------------------------------------------------------------------------