New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drag'n drop support #143

Open
damqui opened this Issue Feb 24, 2015 · 33 comments

Comments

@damqui

damqui commented Feb 24, 2015

hi, I'm considering using ImGui (to convert ooold mfc game tool),
but one lacking thing is the support for drag'n'drop.
Is this feature planned ?
If no, I'll try to hack something in..

thanks for your work !

Ps: Side Question : concerning the uniqueness of Ids, err.. relying on a string crc32 seems weak to me. If I use ImGui for editing data, collisions might occur, and if so, I have no way of knowing if it happened.
Am I missing something ?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Feb 24, 2015

Owner

Do you mean drag and dropping of files into your application window?
That's OS dependant and not likely to be part of core imgui. On Windows you only need to call DragAcceptFiles() and implement the WM_DROPFILES message.

I suppose we can consider to add helpers for common known platforms (such as Windows) to help setting up drag'n drop if there was an easy way to do so. But ImGui doesn't know about concept of windows messages. So it'll probably be a little convoluted for what's essentially replacing 10 lines of code.

If you are asking from the angle of "I may have two widgets with same string", the ID are built from a stack of strings that include the window, tree nodes, and which you can push into. So typically when creating repeated widgets in a loop you need to use PushID/PopID to manipulate the ID stack and make the sub-elements unique.

If you are asking from the angle of "what if even with the precaution above, CRC32 collision occurs ".
It's not strong if you are thinking of long-lived data or are thinking of cryptography. But in practice for the purpose of UI i don't think you'll ever see a problem.

  • Genuine collision are very unlikely in the first place.
  • ID are used to store which widget is active and information about active widget. We are dealing with short-lived live data rather than persistent data, the collision would have to be among two visible widgets, which is a magnitude less likely. And then IF that ever happen, the problem with amount to "when I click button B it clicks button A instead", not very damaging.

In fact, if anything, CRC32 is too slow and we should try to replace it with a faster hash function.

Owner

ocornut commented Feb 24, 2015

Do you mean drag and dropping of files into your application window?
That's OS dependant and not likely to be part of core imgui. On Windows you only need to call DragAcceptFiles() and implement the WM_DROPFILES message.

I suppose we can consider to add helpers for common known platforms (such as Windows) to help setting up drag'n drop if there was an easy way to do so. But ImGui doesn't know about concept of windows messages. So it'll probably be a little convoluted for what's essentially replacing 10 lines of code.

If you are asking from the angle of "I may have two widgets with same string", the ID are built from a stack of strings that include the window, tree nodes, and which you can push into. So typically when creating repeated widgets in a loop you need to use PushID/PopID to manipulate the ID stack and make the sub-elements unique.

If you are asking from the angle of "what if even with the precaution above, CRC32 collision occurs ".
It's not strong if you are thinking of long-lived data or are thinking of cryptography. But in practice for the purpose of UI i don't think you'll ever see a problem.

  • Genuine collision are very unlikely in the first place.
  • ID are used to store which widget is active and information about active widget. We are dealing with short-lived live data rather than persistent data, the collision would have to be among two visible widgets, which is a magnitude less likely. And then IF that ever happen, the problem with amount to "when I click button B it clicks button A instead", not very damaging.

In fact, if anything, CRC32 is too slow and we should try to replace it with a faster hash function.

@damqui

This comment has been minimized.

Show comment
Hide comment
@damqui

damqui Feb 24, 2015

  1. No I mean dragging a widget into another widget, like during edition, for connecting things (putting a file from a file list widget into a file slot of struct widget)

  2. " the ID are built from a stack of strings that include the window, tree nodes, and which you can push into". from the code, the stack of strings is actually a stack of 32 bits values used as the crc seed. I am not really familiar with that "32bit crc as a unique identifier", but the fact that in a editing context, a collision may occur at random without knowing it (except than keeping a list of generated ids for this frame, and testing for collisions at each new id generation), makes me feel it could - randomly - cause edition errors. If such a collision occurs, what happens ? the widget becomes unselectable ? (the first one with same id is selected instead ? in that case, we have a feedback, and that's okay)

damqui commented Feb 24, 2015

  1. No I mean dragging a widget into another widget, like during edition, for connecting things (putting a file from a file list widget into a file slot of struct widget)

  2. " the ID are built from a stack of strings that include the window, tree nodes, and which you can push into". from the code, the stack of strings is actually a stack of 32 bits values used as the crc seed. I am not really familiar with that "32bit crc as a unique identifier", but the fact that in a editing context, a collision may occur at random without knowing it (except than keeping a list of generated ids for this frame, and testing for collisions at each new id generation), makes me feel it could - randomly - cause edition errors. If such a collision occurs, what happens ? the widget becomes unselectable ? (the first one with same id is selected instead ? in that case, we have a feedback, and that's okay)

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Feb 24, 2015

Owner
  1. That's be interesting. Some ideas for a simple version:
  • User pass some information to the draggable object. The information could as simple as a void* associated to a widget. However as a constraint it needs to be able to persist for an extra frame.

The draggable object can behave as a button, but when active it draws itself following the mouse cursor. That needs to be be done by pushing a fullscreen clipping rectangle.

Along with setting g,ActiveId it can set g.DraggingId and g.DraggingData.

(Bonus: It also need to be in a front-most ImDrawList. There's no explicit mechanism for that yet. Things like Tooltip are implicitly high-up in the draw order. Need to improve on explicitly deciding of a sort order. But for a start you don't need that anyway because the parent window of the draggable object will front-most already as soon as you click it. That is, until we implement "hold dragged object on another window to bring it to top". )

  • Drag target can be implemented with something like:

Pseudo-code

bool  IsDraggedInto(void** out_user_data)
{
if (IsItemHovered() && g.DraggingId && mouse_released)
{
   out_user_data = g.DraggingId; 
    return true;
}
return false;
}
  • That needs to be organized with storage so it works regardess of the submission order of draggable object and drag object. e.g. DraggingId persists for one frame after submission and mouse_release, etc.
  1. I don't think there will be a problem. The values in the stack are CRC32 themselves (CRC32 are often seeded this way). The widgets needs to be visible to meaningfully collide. Yes one will be unselectable or clicking on one will trigger the others. The detail may vary depending on the exact widget type. You can easily test by doing:
ImGui::Button("hello");
ImGui::Button("hello");

See what happens.
But I don't think you'll stumble on a accidental / random collision. The only collision you'll realistically fall into will be the common "duplicate label" type of collision which can be solved using ##prefix in strings or PushID/PopID.

Owner

ocornut commented Feb 24, 2015

  1. That's be interesting. Some ideas for a simple version:
  • User pass some information to the draggable object. The information could as simple as a void* associated to a widget. However as a constraint it needs to be able to persist for an extra frame.

The draggable object can behave as a button, but when active it draws itself following the mouse cursor. That needs to be be done by pushing a fullscreen clipping rectangle.

Along with setting g,ActiveId it can set g.DraggingId and g.DraggingData.

(Bonus: It also need to be in a front-most ImDrawList. There's no explicit mechanism for that yet. Things like Tooltip are implicitly high-up in the draw order. Need to improve on explicitly deciding of a sort order. But for a start you don't need that anyway because the parent window of the draggable object will front-most already as soon as you click it. That is, until we implement "hold dragged object on another window to bring it to top". )

  • Drag target can be implemented with something like:

Pseudo-code

bool  IsDraggedInto(void** out_user_data)
{
if (IsItemHovered() && g.DraggingId && mouse_released)
{
   out_user_data = g.DraggingId; 
    return true;
}
return false;
}
  • That needs to be organized with storage so it works regardess of the submission order of draggable object and drag object. e.g. DraggingId persists for one frame after submission and mouse_release, etc.
  1. I don't think there will be a problem. The values in the stack are CRC32 themselves (CRC32 are often seeded this way). The widgets needs to be visible to meaningfully collide. Yes one will be unselectable or clicking on one will trigger the others. The detail may vary depending on the exact widget type. You can easily test by doing:
ImGui::Button("hello");
ImGui::Button("hello");

See what happens.
But I don't think you'll stumble on a accidental / random collision. The only collision you'll realistically fall into will be the common "duplicate label" type of collision which can be solved using ##prefix in strings or PushID/PopID.

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Feb 24, 2015

Owner

About CRC32 collision:
http://stackoverflow.com/questions/14210298/probability-of-collision-crc32
294 hash values : probability of collision 1 in 100000
I think it's a fair risk to take. You rarely have 300 widgets on screen. I haven't seen a collision happen yet. I've used CRC32 to identify data in games and collision happens rarely - we are talking dozens/hundreds of thousands of persistent, long-lived identifiers. Here we talking hundreds of live identifiers in an application that runs interactively (and here I mean interactively as in: slow pace of update).

While it's not impossible that a collision happens my bet is that:

  • you likely will never have one.
  • if you have one you'll likely not notice it because you won't be interacting with the collided id.

For persistent storage such as TreeNode open/close state and Column offsets they are stored per-window so won't collide accross windows, and could potentially be stored in separate maps per usage (e.g. tree nodes vs columns). This is all a non-problem really.

Owner

ocornut commented Feb 24, 2015

About CRC32 collision:
http://stackoverflow.com/questions/14210298/probability-of-collision-crc32
294 hash values : probability of collision 1 in 100000
I think it's a fair risk to take. You rarely have 300 widgets on screen. I haven't seen a collision happen yet. I've used CRC32 to identify data in games and collision happens rarely - we are talking dozens/hundreds of thousands of persistent, long-lived identifiers. Here we talking hundreds of live identifiers in an application that runs interactively (and here I mean interactively as in: slow pace of update).

While it's not impossible that a collision happens my bet is that:

  • you likely will never have one.
  • if you have one you'll likely not notice it because you won't be interacting with the collided id.

For persistent storage such as TreeNode open/close state and Column offsets they are stored per-window so won't collide accross windows, and could potentially be stored in separate maps per usage (e.g. tree nodes vs columns). This is all a non-problem really.

@damqui

This comment has been minimized.

Show comment
Hide comment
@damqui

damqui Feb 25, 2015

For drag'n drop : yes, it might be something as you propose.
Drag'ndrop is really at the heart of some editors ( unity3d, unreal engine 4).
But, if not available, there is still a poor-man way of making "connections" whithout true drag'n drop, using copy-pasting.

For crc, okay, i'll try (though I don't like voodoo :) )

Thanks for your answers.
When I have time, I'll try to convert the generic editor that uses our own data type information to imgui, to see if it is viable.

damqui commented Feb 25, 2015

For drag'n drop : yes, it might be something as you propose.
Drag'ndrop is really at the heart of some editors ( unity3d, unreal engine 4).
But, if not available, there is still a poor-man way of making "connections" whithout true drag'n drop, using copy-pasting.

For crc, okay, i'll try (though I don't like voodoo :) )

Thanks for your answers.
When I have time, I'll try to convert the generic editor that uses our own data type information to imgui, to see if it is viable.

@ocornut ocornut changed the title from drag'n drop support to Drag'n drop support Mar 5, 2015

@ocornut ocornut added the enhancement label Mar 5, 2015

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Apr 13, 2015

Owner

FYI with IsItemActive() and GetMouseDragDelta() there's a few stuff that can already be done now.

I did a quick hacky test for someone who wanted to know if re-ordering was possible:

imgui_dragging

// User state
const int COUNT = 5;
static const char* items_data[COUNT] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
static int items_list[COUNT] = { 0, 1, 2, 3, 4 };

// Render + dragging
for (int n = 0; n < COUNT; n++)
{
    int item_no = items_list[n];
    ImGui::Selectable(items_data[item_no]);

    if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
    {
        float drag_dy = ImGui::GetMouseDragDelta(0).y;
        if (drag_dy < 0.0f && n > 0)
        {
            // Swap
            items_list[n] = items_list[n-1];
            items_list[n-1] = item_no;
            ImGui::ResetMouseDragDelta();
        }
        else if (drag_dy > 0.0f && n < COUNT-1)
        {
            items_list[n] = items_list[n+1];
            items_list[n+1] = item_no;
            ImGui::ResetMouseDragDelta();
        }
    }
}

That could be done in a nicer and more sturdy way within the core library eventually.

Another possibility is to render out of order by manipulating the cursor position, but I think the way above fits better with the typical data structure.

Owner

ocornut commented Apr 13, 2015

FYI with IsItemActive() and GetMouseDragDelta() there's a few stuff that can already be done now.

I did a quick hacky test for someone who wanted to know if re-ordering was possible:

imgui_dragging

// User state
const int COUNT = 5;
static const char* items_data[COUNT] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
static int items_list[COUNT] = { 0, 1, 2, 3, 4 };

// Render + dragging
for (int n = 0; n < COUNT; n++)
{
    int item_no = items_list[n];
    ImGui::Selectable(items_data[item_no]);

    if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
    {
        float drag_dy = ImGui::GetMouseDragDelta(0).y;
        if (drag_dy < 0.0f && n > 0)
        {
            // Swap
            items_list[n] = items_list[n-1];
            items_list[n-1] = item_no;
            ImGui::ResetMouseDragDelta();
        }
        else if (drag_dy > 0.0f && n < COUNT-1)
        {
            items_list[n] = items_list[n+1];
            items_list[n+1] = item_no;
            ImGui::ResetMouseDragDelta();
        }
    }
}

That could be done in a nicer and more sturdy way within the core library eventually.

Another possibility is to render out of order by manipulating the cursor position, but I think the way above fits better with the typical data structure.

@Extrawurst

This comment has been minimized.

Show comment
Hide comment
@Extrawurst

Extrawurst Apr 13, 2015

Contributor

these sort of things shouldn't be buried inside of tickets but be a listed in some kind of wiki

Contributor

Extrawurst commented Apr 13, 2015

these sort of things shouldn't be buried inside of tickets but be a listed in some kind of wiki

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Apr 13, 2015

Owner

Yes definitively, but it's still experimental here, and pretty hacky/broken (won't work with scrolling, in fact it will even exhibit a bug where active items aren't releasing the active flag when they are clipped even when the mouse button is released).

I really want to do a wiki with lots of demos. I wlll eventually.

Owner

ocornut commented Apr 13, 2015

Yes definitively, but it's still experimental here, and pretty hacky/broken (won't work with scrolling, in fact it will even exhibit a bug where active items aren't releasing the active flag when they are clipped even when the mouse button is released).

I really want to do a wiki with lots of demos. I wlll eventually.

ocornut added a commit that referenced this issue Apr 13, 2015

Active widgets is not clipped, so it can always release its active st…
…ate (mentioned in #143)

Otherwise a change in layout moving active widget to a clipped region
may lock the active id.
@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Apr 15, 2015

Owner

@Roflraging posted his own use of dragging to reorder widgets:
https://www.youtube.com/watch?v=pmx4fltxTKg
https://gist.github.com/Roflraging/f4af1d688237a7d367f9

In spite of the extra faff with the copying of his data structures, his approach of recording the index of the hovered fields is more robust and flexible. There's actually very little ImGui code too. I'll rewrite an example using this technique.

I think the post-it could be moved to match the original widget position pretty easily. In one of my test (not published) I actually pushed a no-clipping-rectangle and redrawn the same widget following the mouse, which can also work. Following this base if it's deemed robust enough we can probably come up with helpers.

Owner

ocornut commented Apr 15, 2015

@Roflraging posted his own use of dragging to reorder widgets:
https://www.youtube.com/watch?v=pmx4fltxTKg
https://gist.github.com/Roflraging/f4af1d688237a7d367f9

In spite of the extra faff with the copying of his data structures, his approach of recording the index of the hovered fields is more robust and flexible. There's actually very little ImGui code too. I'll rewrite an example using this technique.

I think the post-it could be moved to match the original widget position pretty easily. In one of my test (not published) I actually pushed a no-clipping-rectangle and redrawn the same widget following the mouse, which can also work. Following this base if it's deemed robust enough we can probably come up with helpers.

@Extrawurst

This comment has been minimized.

Show comment
Hide comment
@Extrawurst

Extrawurst Apr 15, 2015

Contributor

awesome. showing the widget at the mouse is a must-have for drag/drop imho

Contributor

Extrawurst commented Apr 15, 2015

awesome. showing the widget at the mouse is a must-have for drag/drop imho

@Roflraging

This comment has been minimized.

Show comment
Hide comment
@Roflraging

Roflraging Apr 15, 2015

I noticed in the code I copied into the gist that one of the comments was wrong, sorry if anybody was confused! https://gist.github.com/Roflraging/f4af1d688237a7d367f9#file-gistfile1-cpp-L93 https://gist.github.com/Roflraging/f4af1d688237a7d367f9/revisions

Roflraging commented Apr 15, 2015

I noticed in the code I copied into the gist that one of the comments was wrong, sorry if anybody was confused! https://gist.github.com/Roflraging/f4af1d688237a7d367f9#file-gistfile1-cpp-L93 https://gist.github.com/Roflraging/f4af1d688237a7d367f9/revisions

@Extrawurst

This comment has been minimized.

Show comment
Hide comment
@Extrawurst

Extrawurst Feb 9, 2016

Contributor

@ocornut did you rewrite that example that you mentioned ?

Contributor

Extrawurst commented Feb 9, 2016

@ocornut did you rewrite that example that you mentioned ?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Feb 9, 2016

Owner

Sorry I haven't got around to write an example for that (that's why I kept the issue opened for now)

Owner

ocornut commented Feb 9, 2016

Sorry I haven't got around to write an example for that (that's why I kept the issue opened for now)

@Extrawurst

This comment has been minimized.

Show comment
Hide comment
@Extrawurst

Extrawurst Feb 13, 2016

Contributor

I just implemented drag and drop using that concept:
2016-02-13 dragdrop

Contributor

Extrawurst commented Feb 13, 2016

I just implemented drag and drop using that concept:
2016-02-13 dragdrop

@ftsf

This comment has been minimized.

Show comment
Hide comment
@ftsf

ftsf Mar 17, 2016

I'm curious how do you do something on a "drop"?

ftsf commented Mar 17, 2016

I'm curious how do you do something on a "drop"?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Mar 21, 2016

Owner

Right now you would have to track the dragging state, and then on mouse release test for item overlapping the mouse cursor. Something like if (WasDragging && MouseRelease(0) && IsItemHovered())

It could/should probably be wrapped into a helper function but we haven't got a standard way of storing the dragging state yet, so this is left to you to do the test above.

Owner

ocornut commented Mar 21, 2016

Right now you would have to track the dragging state, and then on mouse release test for item overlapping the mouse cursor. Something like if (WasDragging && MouseRelease(0) && IsItemHovered())

It could/should probably be wrapped into a helper function but we haven't got a standard way of storing the dragging state yet, so this is left to you to do the test above.

@paniq

This comment has been minimized.

Show comment
Hide comment
@paniq

paniq Jun 8, 2016

+1 for a working example similar to the GIF above, with perhaps a more complex drag object that demonstrates layouting as well?

What also interests me is highlighting the insertion point upon hover, depending on proximity, or changing a drop targets appearance when someone is dragging over it.

paniq commented Jun 8, 2016

+1 for a working example similar to the GIF above, with perhaps a more complex drag object that demonstrates layouting as well?

What also interests me is highlighting the insertion point upon hover, depending on proximity, or changing a drop targets appearance when someone is dragging over it.

@xaxxon

This comment has been minimized.

Show comment
Hide comment
@xaxxon

xaxxon Jun 12, 2016

Adding something more interesting to the examples would be nice. Currently the only thing I can find is the "dragging" example under "keyboard/mouse/focus" which seems pretty buried - and not very clear as to how it would be useful, unlike the examples in this issue.

Would a patch to the examples code implementing your draggable list example be something you'd be interested in?

xaxxon commented Jun 12, 2016

Adding something more interesting to the examples would be nice. Currently the only thing I can find is the "dragging" example under "keyboard/mouse/focus" which seems pretty buried - and not very clear as to how it would be useful, unlike the examples in this issue.

Would a patch to the examples code implementing your draggable list example be something you'd be interested in?

@VinnyVicious

This comment has been minimized.

Show comment
Hide comment
@VinnyVicious

VinnyVicious Jun 3, 2017

@Extrawurst, are you going to share that snippet? Seems very useful for the imgui community!

VinnyVicious commented Jun 3, 2017

@Extrawurst, are you going to share that snippet? Seems very useful for the imgui community!

@Extrawurst

This comment has been minimized.

Show comment
Hide comment
@Extrawurst

Extrawurst Jun 3, 2017

Contributor

It is not a snippet, it's buried in my engine but it was based on the gist above

Contributor

Extrawurst commented Jun 3, 2017

It is not a snippet, it's buried in my engine but it was based on the gist above

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Oct 17, 2017

Owner

Everyone: I will maybe be able to look at drag'n drop in the upcoming weeks/months, or at least just enough to contemplate using a standard drag'n drop api for Tabs and Docking. Maybe the early versions of course feature won't use a standard drag'n drop api but ultimately this may be the ideal goal so I'll keep that in mind.

If anyone has feedback or request on drag'n drop features please voice them!
Also happy to see your usage (gif, code, etc. whatever you are happy to share or link to).
Tagging for potential feedback @Extrawurst @Roflraging @paniq @nem0.

Owner

ocornut commented Oct 17, 2017

Everyone: I will maybe be able to look at drag'n drop in the upcoming weeks/months, or at least just enough to contemplate using a standard drag'n drop api for Tabs and Docking. Maybe the early versions of course feature won't use a standard drag'n drop api but ultimately this may be the ideal goal so I'll keep that in mind.

If anyone has feedback or request on drag'n drop features please voice them!
Also happy to see your usage (gif, code, etc. whatever you are happy to share or link to).
Tagging for potential feedback @Extrawurst @Roflraging @paniq @nem0.

ocornut added a commit that referenced this issue Oct 20, 2017

IsItemHovered(), IsWindowHovered(): added flags to enable various and…
… more specific behavior. Will enable improvements for popups/context menus and drag'n drop. (relate ~#439, #1013, #143, #925)

The legacy confusing IsItemRectHovered(), IsWindowRectHovered() can be completely removed now.
Changed IsWindowHovered() behavior with default parameter: it now return false is the window is blocked by a popup.
Demo: Added tests for those two functions.

ocornut added a commit that referenced this issue Oct 20, 2017

IsWindowHovered(): Changed default behavior to now return false is a …
…widget from another window is active + Added support for ImGuiHoveredFlags_AllowWhenBlockedByActiveItem. (relate to drag'n drop idoms: #143)
@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Oct 20, 2017

Owner

If you are using drag'n drop idioms in your codebase, please read this thread: #1382

Owner

ocornut commented Oct 20, 2017

If you are using drag'n drop idioms in your codebase, please read this thread: #1382

@Bonicx

This comment has been minimized.

Show comment
Hide comment
@Bonicx

Bonicx Oct 24, 2017

Hi @ocornut I tried using ImGui::TreeNodeEx together with
if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) and have encountered a problem.
I converted your example code of array into std::list style
I loop through std::list<GameObject*> children
image

https://youtu.be/mq94qBWaHrk
As you can see that the reordering only works per item active, hover over, and then drag it down.
I am not sure is it because of my game loop update that causes this issue where I can only move up/down of 1 item per drag and click.

Bonicx commented Oct 24, 2017

Hi @ocornut I tried using ImGui::TreeNodeEx together with
if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) and have encountered a problem.
I converted your example code of array into std::list style
I loop through std::list<GameObject*> children
image

https://youtu.be/mq94qBWaHrk
As you can see that the reordering only works per item active, hover over, and then drag it down.
I am not sure is it because of my game loop update that causes this issue where I can only move up/down of 1 item per drag and click.

ocornut added a commit that referenced this issue Oct 25, 2017

Comments about mouse setup and clearing HoveredWindow when mouse down…
… isn't owned by imgui (will affect some future hovered test and drag'n drop patterns) (#143, #1382, #1392)

@ocornut ocornut added the in progress label Nov 2, 2017

ocornut added a commit that referenced this issue Nov 2, 2017

Drag and Drop: Made it legal to not call SetDragDropPayload() between…
… BeginDragDropSource() and EndDragDropSource(). (#143)

ocornut added a commit that referenced this issue Nov 6, 2017

ocornut added a commit that referenced this issue Nov 9, 2017

ocornut added a commit that referenced this issue Nov 13, 2017

ocornut added a commit that referenced this issue Nov 30, 2017

ocornut added a commit that referenced this issue Dec 8, 2017

Drag and drop: Added COL3F payload for color without alpha overwrite.…
… Exposed standard color payload types in imgui.h (#143)

ocornut added a commit that referenced this issue Dec 8, 2017

ocornut added a commit that referenced this issue Dec 12, 2017

ocornut added a commit that referenced this issue Dec 12, 2017

Drag and Drop: Drop target infer a fallback ID from the rectangle. Av…
…oid Preview being accepted on drop frame when drop target has no ID. (#143)
@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Dec 12, 2017

Owner

I have merged the drag_and_drop branch into master now.

I consider the feature Beta (and marked it as such in imgui.h) as it is missing a few thing:
A) There's no real demo yet (but the color widgets now support drag and drop). That's easy to fix, will work on demo code soonish.
B) I haven't established that the api will be good enough to handle re-ordering/moving nodes, in a list or in a tree.
C) I haven't established that the api will be good enough to build a bridge with OS native drag'n drop.

When we have proof-on-concepts to confirm that both B and C are faisible I'll mark the api as non-beta.
If someone is interested in tackling either for their project let me know.

Owner

ocornut commented Dec 12, 2017

I have merged the drag_and_drop branch into master now.

I consider the feature Beta (and marked it as such in imgui.h) as it is missing a few thing:
A) There's no real demo yet (but the color widgets now support drag and drop). That's easy to fix, will work on demo code soonish.
B) I haven't established that the api will be good enough to handle re-ordering/moving nodes, in a list or in a tree.
C) I haven't established that the api will be good enough to build a bridge with OS native drag'n drop.

When we have proof-on-concepts to confirm that both B and C are faisible I'll mark the api as non-beta.
If someone is interested in tackling either for their project let me know.

@heroboy

This comment has been minimized.

Show comment
Hide comment
@heroboy

heroboy Dec 13, 2017

Contributor

Cool! I think it is interesting if I can copy a draw list as the content of dragging source. Currently clone a draw list is hard.
default

Contributor

heroboy commented Dec 13, 2017

Cool! I think it is interesting if I can copy a draw list as the content of dragging source. Currently clone a draw list is hard.
default

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Dec 13, 2017

Owner

To clone a ImDrawList you need to deep copy the fields, I think ImVector is missing a = operator we could add one.

But may I ask why are you trying to do that?

Owner

ocornut commented Dec 13, 2017

To clone a ImDrawList you need to deep copy the fields, I think ImVector is missing a = operator we could add one.

But may I ask why are you trying to do that?

@heroboy

This comment has been minimized.

Show comment
Hide comment
@heroboy

heroboy Dec 14, 2017

Contributor

_Channels[0] should use memcpy. Looklike _ClipRectStack should not be copied.

But may I ask why are you trying to do that?

  1. Make an effect that dragging a chrome or firefox tab.
  2. Reuse the drawlist to avoid run the code that processing the user interaction.
  3. Add some effect. Scaling or fading out along y axis.
Contributor

heroboy commented Dec 14, 2017

_Channels[0] should use memcpy. Looklike _ClipRectStack should not be copied.

But may I ask why are you trying to do that?

  1. Make an effect that dragging a chrome or firefox tab.
  2. Reuse the drawlist to avoid run the code that processing the user interaction.
  3. Add some effect. Scaling or fading out along y axis.

ocornut added a commit that referenced this issue Dec 15, 2017

ocornut added a commit that referenced this issue Dec 15, 2017

Drag and Drop: Source can also inhibit the preview on target, useful …
…for extern sources that only exists for one frame. (#143)

ocornut added a commit that referenced this issue Dec 15, 2017

@evan-gordon

This comment has been minimized.

Show comment
Hide comment
@evan-gordon

evan-gordon Dec 15, 2017

Hey @ocornut
Is there an ETA on drag and drop support for the navigation branch?
I'm trying to use this for item swapping inside the player inventory for my game. For now I've been tinkering with your code to get it working on my end, but as it is now a lot of your code is pretty heavily tied to mouse use.

evan-gordon commented Dec 15, 2017

Hey @ocornut
Is there an ETA on drag and drop support for the navigation branch?
I'm trying to use this for item swapping inside the player inventory for my game. For now I've been tinkering with your code to get it working on my end, but as it is now a lot of your code is pretty heavily tied to mouse use.

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Dec 15, 2017

Owner

@evan-gordon It's already merged into the navigation branch, but isn't the point of drag and drop to a be mouse thing? Most of the api makes no sense with keyboard.

You can use custom code if you need custom control, which is likely if you want the way of using dear imgui for in-game stuff. Humble reminder that dear imgui isn't designed for that use case at all.

Owner

ocornut commented Dec 15, 2017

@evan-gordon It's already merged into the navigation branch, but isn't the point of drag and drop to a be mouse thing? Most of the api makes no sense with keyboard.

You can use custom code if you need custom control, which is likely if you want the way of using dear imgui for in-game stuff. Humble reminder that dear imgui isn't designed for that use case at all.

@evan-gordon

This comment has been minimized.

Show comment
Hide comment
@evan-gordon

evan-gordon Dec 16, 2017

Okay fair enough, I'm not against writing the new code myself as i already started.

evan-gordon commented Dec 16, 2017

Okay fair enough, I'm not against writing the new code myself as i already started.

ocornut added a commit that referenced this issue Dec 22, 2017

Drag and Drop: Disable tracking mouse button ownership when an extern…
…al drag source is active, to make it easier to achieve drag and drop over multiple OS windows. (#143)

wflohry added a commit to wflohry/imgui-addons that referenced this issue Dec 27, 2017

Drag and Drop: Disable tracking mouse button ownership when an extern…
…al drag source is active, to make it easier to achieve drag and drop over multiple OS windows. (#143)

ocornut added a commit that referenced this issue Dec 28, 2017

ocornut added a commit that referenced this issue Jan 18, 2018

ocornut added a commit that referenced this issue Jan 18, 2018

ocornut added a commit that referenced this issue Feb 6, 2018

Drag and Drop: TreeNode as drop target displays rectangle over full f…
…rame. Added optional internal storage for item display rect. Will expose later. (#1597, #143)

ocornut added a commit that referenced this issue Feb 21, 2018

Drag and Drop: BeginDragDropSource(): temporarily removed the optiona…
…l mouse_button=0 parameter because it is really usable in typical conditions at the moment. (#143, #1637)

ocornut added a commit that referenced this issue Mar 8, 2018

@sherief

This comment has been minimized.

Show comment
Hide comment
@sherief

sherief Apr 11, 2018

I have been spending some time on drag-and-drop and I think a write up is in order.

I think I can split interest in drag-and-drop into two categories:

  • "Good Enough" drag-and-drop, for lack of a better name. Just to be clear, I don't mean this in a derogatory way whatsoever - I just use that to refer to the case where drag and drop is only used within the same app, process, and address space. Things like dragging from a color picker to a scene object or attribute, basically only from imgui to imgui and within the same app.
  • "First Class" drag-and-drop where you interact with the system just like a native app. You work with clipboard trackers and "drawer" apps like Yoink on macOS (which lets you drag things into a "shelf" that you can drag them out of later). This needs platform dependent code and idioms, and needs to support a level of complexity that most users of Good Enough DnD neither need nor care about - things like multiple formats for the same drag (dragging text as plain and rich text for example) and drag "promises" where the drag source doesn't have the data ready, but only creates it on drop due to the creation being expensive.

Both cases outlined in the First Class DnD description above are concrete things that I ran into - in one tool I support dragging objects within the same instance and across instances, and also to other external editors (paint.exe for textures, etc.). In the former case I need multiple drag formats (I drag the object as both an in-process pointer and a "promise" to generate a serialized version, for whether the drag ends inside or outside the same process), and the drag also exposes a filesystem path that's used when it's dropped on paint.exe. I absolutely need First Class DnD support, and prior to this branch / API I had it hacked into an earlier version of imgui. I can go on forever about NSPasteboard and IDataObjects in a multithreaded app but it'd probably be overkill.

The Good Enough case seems to be sufficiently covered by the current API, but for First Class DnD support I think some changes need to be made:

  • Make payload setting optional. Right now I set a dummy payload to make the API happy, but I think the API could still work while assuming the payload setting can be omitted. In my case I directly trigger an OS drag and drop, and that works with the current API, but the drag target begin APIs don't play nicely when you have a drag with no payload.
  • Have some way to know when a drag begins, that triggers exactly once for every drag. Right now BeginDragDropSource() returns true every frame that the drag is active, and it's up to the app to detect whether the drag did begin this frame or not. This is a bit of a mismatch between how the OS DnD APIs work in "retained mode" vs imgui's immediate mode. I think it'd be an acceptable break from immediacy for imgui to track this state, in the same way it tracks double clicks and triggers them for a single frame (IIRC).

I'm interested in hearing any comments and answering any questions you might have. First Class DnD support is extremely important to me and the tool I'm working on exercises some less frequently used paths (including running the OS DnD code async in a separate thread).

sherief commented Apr 11, 2018

I have been spending some time on drag-and-drop and I think a write up is in order.

I think I can split interest in drag-and-drop into two categories:

  • "Good Enough" drag-and-drop, for lack of a better name. Just to be clear, I don't mean this in a derogatory way whatsoever - I just use that to refer to the case where drag and drop is only used within the same app, process, and address space. Things like dragging from a color picker to a scene object or attribute, basically only from imgui to imgui and within the same app.
  • "First Class" drag-and-drop where you interact with the system just like a native app. You work with clipboard trackers and "drawer" apps like Yoink on macOS (which lets you drag things into a "shelf" that you can drag them out of later). This needs platform dependent code and idioms, and needs to support a level of complexity that most users of Good Enough DnD neither need nor care about - things like multiple formats for the same drag (dragging text as plain and rich text for example) and drag "promises" where the drag source doesn't have the data ready, but only creates it on drop due to the creation being expensive.

Both cases outlined in the First Class DnD description above are concrete things that I ran into - in one tool I support dragging objects within the same instance and across instances, and also to other external editors (paint.exe for textures, etc.). In the former case I need multiple drag formats (I drag the object as both an in-process pointer and a "promise" to generate a serialized version, for whether the drag ends inside or outside the same process), and the drag also exposes a filesystem path that's used when it's dropped on paint.exe. I absolutely need First Class DnD support, and prior to this branch / API I had it hacked into an earlier version of imgui. I can go on forever about NSPasteboard and IDataObjects in a multithreaded app but it'd probably be overkill.

The Good Enough case seems to be sufficiently covered by the current API, but for First Class DnD support I think some changes need to be made:

  • Make payload setting optional. Right now I set a dummy payload to make the API happy, but I think the API could still work while assuming the payload setting can be omitted. In my case I directly trigger an OS drag and drop, and that works with the current API, but the drag target begin APIs don't play nicely when you have a drag with no payload.
  • Have some way to know when a drag begins, that triggers exactly once for every drag. Right now BeginDragDropSource() returns true every frame that the drag is active, and it's up to the app to detect whether the drag did begin this frame or not. This is a bit of a mismatch between how the OS DnD APIs work in "retained mode" vs imgui's immediate mode. I think it'd be an acceptable break from immediacy for imgui to track this state, in the same way it tracks double clicks and triggers them for a single frame (IIRC).

I'm interested in hearing any comments and answering any questions you might have. First Class DnD support is extremely important to me and the tool I'm working on exercises some less frequently used paths (including running the OS DnD code async in a separate thread).

@ocornut ocornut added the drag drop label Apr 11, 2018

ocornut added a commit that referenced this issue May 25, 2018

Drag and Drop: Source doesn't report as hovered (at a lower-level). S…
…ource disable AllowOverlap flag if any set. (#143)

ocornut added a commit that referenced this issue May 28, 2018

ocornut added a commit that referenced this issue May 28, 2018

Made drag source tooltip display at the same position as a regular to…
…oltip to avoid discontinuity where dynamically swapping tooltip at the target site. Made drag source tooltip override previous tooltip if any. (#1739, #143).

ocornut added a commit that referenced this issue May 28, 2018

Internals: Added BeginDragDropTooltip() internal function to convey s…
…emantic (drag and drop tooltip doesn't get clipped within display boundaries). Revert part of 3218666. (#1739, #143).

ocornut added a commit that referenced this issue Jul 8, 2018

Drag and Drop: Fixed an incorrect assert when dropping a source that …
…is submitted after the target (bug introduced with 1.62 changes related to the addition of IsItemDeactivated()). (#1875, #143)

ocornut added a commit that referenced this issue Jul 8, 2018

Drag and Drop: Fixed ImGuiDragDropFlags_SourceNoDisableHover to affec…
…t hovering state prior to calling IsItemHovered() + fixed description. (#143)

ocornut added a commit that referenced this issue Jul 8, 2018

Drag and Drop: Calling BeginTooltip() between a BeginDragSource()/End…
…DragSource() or BeginDropTarget()/EndDropTarget() uses adjusted tooltip settings matching the one created when calling BeginDragSource() without the ImGuiDragDropFlags_SourceNoPreviewTooltip flag. (#143) + additional safety checks.

ocornut added a commit that referenced this issue Jul 8, 2018

ocornut added a commit that referenced this issue Jul 31, 2018

Drag and Drop: Clear payload buffers more consistently in ClearDragAn…
…dDrop() + BeginDragDropTargetCustom() can't succeed with hidden contents. (#143)

ocornut added a commit that referenced this issue Jul 31, 2018

Drag and Drop: Added ImGuiDragDropFlags_SourceAutoExpirePayload flag …
…to force payload to expire if the source stops being submitted. (#1725, #143).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment