How to Implement Drag and Drop Functionality Between ImGui Windows
TLDR: Dear ImGui provides a global drag-and-drop subsystem that works across all windows by storing payload state in the global ImGuiContext, allowing any window to accept drops from any other window within the same context.
Implementing drag and drop functionality between ImGui windows relies on Dear ImGui's global state management rather than window-specific handling. According to the ocornut/imgui source code, the drag-and-drop system stores active payloads in the global ImGuiContext defined in imgui.cpp, making cross-window transfers seamless as long as both windows belong to the same context. This architecture means you use the same API calls whether dragging within a single window or between separate windows.
Understanding the Global Drag-and-Drop State
The drag-and-drop state (g.DragDropActive, g.DragDropPayload) lives in the global ImGuiContext. When a source in Window A activates a drag via BeginDragDropSource(), the context stores the source ID and payload data. Any subsequent call to BeginDragDropTarget() in Window B consults this same global state, allowing the payload to be accepted regardless of which window created it. The only requirement is that the target's hover test succeeds and the target ID differs from the source ID.
Setting Up the Drag Source
To initiate a drag operation, call ImGui::BeginDragDropSource() on the source widget. In imgui.cpp, this function validates the item ID and activates the global drag state.
If the widget lacks a unique identifier (such as Text() or Image()), you must pass the ImGuiDragDropFlags_SourceAllowNullID flag:
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
// Drag logic here
ImGui::EndDragDropSource();
}
Configuring the Payload Data
While the source is active, call ImGui::SetDragDropPayload() exactly once to supply the data. This function, implemented in imgui.cpp, copies the payload into internal buffers and records the type string (maximum 32 characters):
const char* payload_data = "Cross-window data";
ImGui::SetDragDropPayload("MY_TYPE", payload_data, strlen(payload_data) + 1);
Call ImGui::EndDragDropSource() to finalize the operation. This hides the preview tooltip and discards the payload if it was never accepted.
Creating Cross-Window Drop Targets
In the destination window, call ImGui::BeginDragDropTarget() to declare a drop zone. According to the source code in imgui.cpp, this verifies that the mouse is hovering a valid item (the ImGuiItemStatusFlags_HoveredRect flag) and that the target ID differs from the source ID. If the target item has no ID, ImGui creates a temporary one from the item's rectangle.
Accepting and Validating Payloads
Use ImGui::AcceptDragDropPayload() to check for compatible payload types:
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_TYPE")) {
const char* data = (const char*)payload->Data;
// Process data here
}
ImGui::EndDragDropTarget();
}
Call ImGui::EndDragDropTarget() to clear the target state and discard the delivered payload immediately.
Controlling Visual Feedback
ImGui automatically draws a default drop rectangle when hovering valid targets. To disable this, use the ImGuiDragDropFlags_AcceptNoDrawDefaultRect flag. You can also suppress the source preview tooltip using ImGuiDragDropFlags_SourceNoPreviewTooltip or ImGuiDragDropFlags_AcceptNoPreviewTooltip when specific visual customization is required.
Complete Cross-Window Implementation Example
The following example demonstrates drag and drop between two separate ImGui windows, as referenced in imgui_demo.cpp:
// Window A - Drag Source
if (ImGui::Begin("Source Window")) {
ImGui::Text("Drag the button below:");
if (ImGui::Button("Drag Me")) {
// Button activation handles the item ID
}
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
const char* payload = "Hello from Source";
ImGui::SetDragDropPayload("MY_STRING", payload, strlen(payload) + 1);
ImGui::TextColored(ImVec4(1,1,0,1), "Dragging...");
ImGui::EndDragDropSource();
}
ImGui::End();
}
// Window B - Drop Target
if (ImGui::Begin("Target Window")) {
ImGui::Text("Drop here:");
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* pl = ImGui::AcceptDragDropPayload("MY_STRING")) {
const char* received = (const char*)pl->Data;
ImGui::Text("Received: %s", received);
}
ImGui::EndDragDropTarget();
}
ImGui::End();
}
This works across windows because both access the same global ImGuiContext drag state, allowing seamless transfer of the "MY_STRING" payload type.
Summary
- Global State: Drag-and-drop data lives in
ImGuiContext(g.DragDropActive,g.DragDropPayload), enabling cross-window transfers between any windows sharing the same context. - Source Setup: Use
BeginDragDropSource()andSetDragDropPayload()to initiate drags; includeImGuiDragDropFlags_SourceAllowNullIDfor widgets without unique IDs. - Target Setup: Use
BeginDragDropTarget()andAcceptDragDropPayload()to receive data; the function validates payload types against the 32-character type identifier. - Cleanup: Always call
EndDragDropSource()andEndDragDropTarget()to properly manage payload lifecycle and visual feedback. - Core Files: Implementation details are found in
imgui.cpp(core logic) andimgui_demo.cpp(reference examples), with declarations inimgui.h.
Frequently Asked Questions
Can I drag and drop between ImGui windows that are in different contexts?
No. The drag-and-drop system relies on global state stored in ImGuiContext. If windows belong to different contexts (rare in standard applications), they cannot share drag payloads. Ensure both windows use the same context by calling ImGui::SetCurrentContext() with the same pointer, or stick to the default global context.
What happens if the source widget doesn't have a unique ID?
Widgets like ImGui::Text() or ImGui::Image() lack automatic IDs. According to the source in imgui.cpp, you must pass ImGuiDragDropFlags_SourceAllowNullID to BeginDragDropSource(). Without this flag, the function will assert or fail because it cannot track the drag source properly.
How long does the payload data remain valid?
The payload remains valid from the call to SetDragDropPayload() until EndDragDropSource() completes (if rejected) or EndDragDropTarget() executes (if accepted). The data is copied into ImGui's internal buffers during SetDragDropPayload(), so you don't need to maintain the original variable throughout the drag operation.
Can I customize the visual feedback during drag-and-drop operations?
Yes. Use ImGuiDragDropFlags_SourceNoPreviewTooltip to suppress the default tooltip preview, or render custom content between BeginDragDropSource() and EndDragDropSource(). For drop targets, use ImGuiDragDropFlags_AcceptNoDrawDefaultRect to prevent the default rectangle, then draw your own highlight using ImGui::GetItemRectMin() and ImGui::GetItemRectMax().
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →