ImGUI drag'n'drop

While trying to create a mechanic, I decided to try to do it via Drag’n’Drop. And was surprised to find that in the original ImGUI it exists by default, but in Gamma there are only DragDropFlags.

Am I missing something or has it just not been imported? Is this theoretically possible or is there some difficulty that prevents it from being imported?

Demo Code
    IMGUI_DEMO_MARKER("Widgets/Drag and drop");
    if (ImGui::TreeNode("Drag and Drop"))
    {
        IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets");
        if (ImGui::TreeNode("Drag and drop in standard widgets"))
        {
            // ColorEdit widgets automatically act as drag source and drag target.
            // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
            // to allow your own widgets to use colors in their drag and drop interaction.
            // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
            HelpMarker("You can drag from the color squares.");
            static float col1[3] = { 1.0f, 0.0f, 0.2f };
            static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
            ImGui::ColorEdit3("color 1", col1);
            ImGui::ColorEdit4("color 2", col2);
            ImGui::TreePop();
        }

        IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items");
        if (ImGui::TreeNode("Drag and drop to copy/swap items"))
        {
            enum Mode
            {
                Mode_Copy,
                Mode_Move,
                Mode_Swap
            };
            static int mode = 0;
            if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();
            if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();
            if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; }
            static const char* names[9] =
            {
                "Bobby", "Beatrice", "Betty",
                "Brianna", "Barry", "Bernard",
                "Bibi", "Blaine", "Bryn"
            };
            for (int n = 0; n < IM_ARRAYSIZE(names); n++)
            {
                ImGui::PushID(n);
                if ((n % 3) != 0)
                    ImGui::SameLine();
                ImGui::Button(names[n], ImVec2(60, 60));

                // Our buttons are both drag sources and drag targets here!
                if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
                {
                    // Set payload to carry the index of our item (could be anything)
                    ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int));

                    // Display preview (could be anything, e.g. when dragging an image we could decide to display
                    // the filename and a small preview of the image, etc.)
                    if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); }
                    if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); }
                    if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); }
                    ImGui::EndDragDropSource();
                }
                if (ImGui::BeginDragDropTarget())
                {
                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL"))
                    {
                        IM_ASSERT(payload->DataSize == sizeof(int));
                        int payload_n = *(const int*)payload->Data;
                        if (mode == Mode_Copy)
                        {
                            names[n] = names[payload_n];
                        }
                        if (mode == Mode_Move)
                        {
                            names[n] = names[payload_n];
                            names[payload_n] = "";
                        }
                        if (mode == Mode_Swap)
                        {
                            const char* tmp = names[n];
                            names[n] = names[payload_n];
                            names[payload_n] = tmp;
                        }
                    }
                    ImGui::EndDragDropTarget();
                }
                ImGui::PopID();
            }
            ImGui::TreePop();
        }

I have done a proof of concept but it is difficult to transfer data as references are used. The main problem seems to be in the payload references.

Maybe we can finish it together?

imgui_dragdrop.vl (14.8 KB)
VL.ImGui.vl (463.4 KB)

Very please, let’s work on it, shall we?
It’s just that I want to make an editor that is almost impossible to build without this functionality (or it will be ugly). And writing my own drag and drop system would be crazy.

A better and usable proof of concept
Maybe something like this would satisfy my needs (but it would still be cool to have this in the main api)

ImguiDragDrop.vl (52.3 KB)
ImguiDragDropExample.vl (28.8 KB)

Animation

2 Likes

Made it possible to pass objects, not just a string. I’m sure when this is implemented in the main API, the lovely developers will do a cleaner and better job. But if it hasn’t yet, this is a great way to implement such functionality in your application.


ImguiDragDropExampleO.vl (35.0 KB)
ImguiDragDrop.vl (87.7 KB)

2 Likes

Interesting topic. Unfortunately, I can’t help with development but I wanted to point out a strange behavior. I don’t understand if there’s a bug or if I’m missing something. Does it work for you like the Little Drag and Drop demo?
Here how it works on my pc.

drag&drop-strange-behavior
?

Hi @enzodan

Not sure, what exactly is bothering you?
This is a very rough example to show that the mechanics work. If you want mechanics like the one in the gif above (the one with the tiles), then you need to implement it in your app.

Hi @jar.
I thought the example already allowed the drag action in the receive section replacing the default element already present.
Thanks anyway.

@enzodan Yes, now I understand.

I didn’t make a clearer example as this is generally intended for more advanced users. I really hope this will be built into the main API and then I will be willing to make a clearer example.

If you really need this functionality, let me know and I will try to make the example clearer.