ImGui Drag and Drop Between Windows Implementation: Complete Guide
Dear ImGui implements cross-window drag and drop through a global context state (g.DragDropActive) that persists across all windows in the same ImGuiContext, allowing any window to accept payloads from any other window without additional configuration.
The ocornut/imgui repository provides a unified drag-and-drop API that functions identically whether the source and target widgets reside in the same window or different ones. Because the drag state lives in the global context rather than individual windows, implementing ImGui drag and drop between windows requires only the standard source-target pattern without window-specific handling.
How Cross-Window Drag and Drop Works
The drag-and-drop subsystem stores all active state—including the payload buffer, source ID, and target candidates—in the global ImGuiContext structure. When you call ImGui::BeginDragDropSource() in Window A, the library sets g.DragDropActive to true and caches the payload data. Subsequent calls to ImGui::BeginDragDropTarget() in Window B query this same global state, validating hover rectangles and payload types against the stored source information.
This architecture means the only requirement for cross-window compatibility is that both windows belong to the same ImGui context. The source and target need not know about each other’s existence; the context mediates all communication automatically.
Implementing the Drag Source
To initiate a drag operation that can be received by any window, wrap your source widget with ImGui::BeginDragDropSource() and ImGui::EndDragDropSource().
Starting the Drag Operation
Call ImGui::BeginDragDropSource() immediately after the item you want to make draggable. According to the source code in imgui.cpp, this function validates the item’s ImGuiID and activates the global drag state【link-15016】.
For widgets without unique identifiers—such as ImGui::Text() or ImGui::Image()—pass the ImGuiDragDropFlags_SourceAllowNullID flag to allow dragging from items that would otherwise be ignored by the ID system.
Setting the Payload
While the source is active, you must call ImGui::SetDragDropPayload() exactly once to copy your data into ImGui’s internal buffer:
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
const char* my_data = "Cross-window payload";
ImGui::SetDragDropPayload("MY_TYPE", my_data, strlen(my_data) + 1);
ImGui::Text("Dragging: %s", my_data);
ImGui::EndDragDropSource();
}
The SetDragDropPayload function, implemented in imgui.cpp, copies the data to a heap or local buffer and records the type string (maximum 32 characters)【link-15150】. The payload remains valid until ImGui::EndDragDropSource() is called or a target accepts the data【link-15135】.
Source Flags
ImGuiDragDropFlags_SourceAllowNullID: Enables dragging from widgets without an ID (e.g., text labels or images).ImGuiDragDropFlags_SourceNoPreviewTooltip: Suppresses the default tooltip that follows the cursor during the drag.
Implementing the Drop Target
Any window can act as a drop target by calling ImGui::BeginDragDropTarget() on the item that should receive the payload.
Declaring the Target
In imgui.cpp, BeginDragDropTarget() checks that the mouse hovers the target item’s rectangle (verified by the ImGuiItemStatusFlags_HoveredRect flag) and that the target’s ID differs from the source’s ID. If the target widget lacks an ID, ImGui generates a temporary one from the item’s screen rectangle【link-15264】.
if (ImGui::BeginDragDropTarget()) {
// Target is active and accepting payloads
ImGui::EndDragDropTarget();
}
Accepting the Payload
Use ImGui::AcceptDragDropPayload() to validate the payload type and retrieve the data pointer:
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_TYPE")) {
const char* received_data = (const char*)payload->Data;
// Process data
}
This function, located in imgui.cpp, validates the type string, registers the acceptance with the global context, and draws a default drop rectangle highlight unless you specify ImGuiDragDropFlags_AcceptNoDrawDefaultRect【link-15308】. After ImGui::EndDragDropTarget() returns, the payload is cleared from the global state if it was delivered【link-15364】.
Complete Cross-Window Example
The following implementation demonstrates dragging a string payload from "Source Window" to "Target Window" using the global context:
// Source Window
if (ImGui::Begin("Source Window")) {
ImGui::Button("Drag Me");
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
const char* payload_str = "Hello from Window A";
ImGui::SetDragDropPayload("STRING_PAYLOAD", payload_str, strlen(payload_str) + 1);
ImGui::Text("Dragging to another window...");
ImGui::EndDragDropSource();
}
ImGui::End();
}
// Target Window
if (ImGui::Begin("Target Window")) {
ImGui::Text("Drop payload here:");
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("STRING_PAYLOAD")) {
IM_ASSERT(payload->DataSize == strlen((const char*)payload->Data) + 1);
ImGui::Text("Received: %s", (const char*)payload->Data);
}
ImGui::EndDragDropTarget();
}
ImGui::End();
}
This example appears in various forms in imgui_demo.cpp, where multiple demo windows showcase dragging colors and text between separate windows【link-demo-1689】.
Key Configuration Flags
Control visual feedback and validation behavior using these flags from imgui.h:
| Flag | Effect |
|---|---|
ImGuiDragDropFlags_SourceAllowNullID |
Allows dragging from widgets without identifiers (e.g., Text(), Image()). |
ImGuiDragDropFlags_SourceNoPreviewTooltip |
Hides the default preview tooltip during the drag operation. |
ImGuiDragDropFlags_SourceNoHoldToOpenOthers |
Prevents holding the drag from opening tree nodes or collapsing headers. |
ImGuiDragDropFlags_AcceptNoDrawDefaultRect |
Prevents ImGui from drawing the default rectangle around the target. |
ImGuiDragDropFlags_AcceptNoPreviewTooltip |
Suppresses the source’s preview tooltip when hovering this specific target. |
ImGuiDragDropFlags_AcceptPeekOnly |
Allows peeking at the payload without consuming it (returns payload but does not mark as delivered). |
Summary
- Global State: Drag-and-drop data lives in
ImGuiContext(g.DragDropActive,g.DragDropPayload), making cross-window transfers automatic. - Source Pattern: Call
BeginDragDropSource(),SetDragDropPayload(), andEndDragDropSource()on the draggable widget. - Target Pattern: Call
BeginDragDropTarget(),AcceptDragDropPayload(), andEndDragDropTarget()on the receiving widget. - No Window References: The API never requires passing window pointers; the context handles routing between any windows in the same context.
- Reference Implementation: See
imgui.cppfor the core logic andimgui_demo.cppfor working examples of multi-window drag operations.
Frequently Asked Questions
Does ImGui drag and drop require special setup for multiple windows?
No. The drag-and-drop system operates entirely through the global ImGuiContext. As long as both windows are rendered within the same context (the default behavior), payloads automatically transfer between them without additional configuration or window handles.
What happens if the target window is behind the source window?
The payload remains active as long as the mouse button is held. If the target window is behind the source, standard ImGui window focus behavior applies—clicking or dragging over the target window will bring it to the front (depending on your window focus settings), and the drop will register normally if the hover test succeeds.
Can I drag from a widget that doesn't have an ID?
Yes. Pass the ImGuiDragDropFlags_SourceAllowNullID flag to BeginDragDropSource(). This is necessary for widgets like ImGui::Text() or ImGui::Image() that do not generate unique identifiers automatically. Without this flag, BeginDragDropSource() returns false for ID-less items【link-15016】.
How long does the payload data remain valid?
The payload persists until either the source calls EndDragDropSource() or the target calls EndDragDropTarget() after accepting the payload. Internally, ImGui copies your data to an internal buffer during SetDragDropPayload(), so the original pointer need not remain valid after the call【link-15150】.
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 →