Skip to content
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

Closed
damqui opened this issue Feb 24, 2015 · 40 comments
Closed

Drag'n drop support #143

damqui opened this issue Feb 24, 2015 · 40 comments
Labels

Comments

@damqui
Copy link

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
Copy link
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
Copy link
Author

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
Copy link
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
Copy link
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
Copy link
Author

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 drag'n drop support Drag'n drop support Mar 5, 2015
@ocornut
Copy link
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
Copy link
Contributor

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

@ocornut
Copy link
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
…ate (mentioned in #143)

Otherwise a change in layout moving active widget to a clipped region
may lock the active id.
@ocornut
Copy link
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
Copy link
Contributor

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

@unpacklo
Copy link

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
Copy link
Contributor

@ocornut did you rewrite that example that you mentioned ?

@ocornut
Copy link
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
Copy link
Contributor

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

@ftsf
Copy link

ftsf commented Mar 17, 2016

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

@ocornut
Copy link
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
Copy link

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
Copy link

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
Copy link

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

@extrawurst
Copy link
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
Copy link
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
… 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
…widget from another window is active + Added support for ImGuiHoveredFlags_AllowWhenBlockedByActiveItem. (relate to drag'n drop idoms: #143)
@ocornut
Copy link
Owner

ocornut commented Oct 20, 2017

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

ocornut added a commit that referenced this issue May 28, 2018
ocornut added a commit that referenced this issue May 28, 2018
…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
…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
…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
…t hovering state prior to calling IsItemHovered() + fixed description. (#143)
ocornut added a commit that referenced this issue Jul 8, 2018
…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 31, 2018
…dDrop() + BeginDragDropTargetCustom() can't succeed with hidden contents. (#143)
ocornut added a commit that referenced this issue Jul 31, 2018
…to force payload to expire if the source stops being submitted. (#1725, #143).
ocornut added a commit that referenced this issue Oct 1, 2018
@s-ol
Copy link

s-ol commented Oct 3, 2018

This is working super well for me in it's current state.

A small note is that the held-item preview tooltip turns into ... when the DragDropSource goes out of scope. I understand that this is a really difficult design problem to properly solve with the immediate-mode UI approach, but trying out the new docking branch I noticed that this situation might become common as dragging and dropping from different tabs inside the same docking node seems like something you would want to do to me.

I am not sure if that would violate some ImGui internal rules or limitations, but couldn't the draw list for the node be cached? That behavior could be toggle-able via a Source-DragDropFlag in case you know that you would rather want no tooltip than a non-realtime one.

@ocornut
Copy link
Owner

ocornut commented Oct 3, 2018

Hello @s-ol. This problem is discussed in #1725. I think a better solution would be to allow the user to peak into the payload and submit the tooltip outside of the BeginDragSource() scope.

@ChugunovRoman
Copy link

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.

Hi.
Is dropping of files into a app window does support?
How to implement this?

@rokups
Copy link
Contributor

rokups commented Oct 1, 2019

@ChugunovRoman this is backend-specific. For example SDL sends SDL_DROPFILE when file is dropped on to window. You have to handle this event. You most likely also want to check mouse cursor at the time event is sent in order to detect on which control it was dropped.

@ocornut shouldnt this issue be closed? Drag & drop is implemented.

@ocornut
Copy link
Owner

ocornut commented Oct 1, 2019

I tend to keep issue opens when some of the posts are discussing interesting/useful ideas that are still yet to implement and not covered elsewhere.

There are a few things discussed here that are still unfullfilled:

I'll close this issue as the points raised above are all listed in TODO.txt, when there is an impulse/attempt to solve them it we can open a new issue to discuss them.

Thanks all!

@rokups
Copy link
Contributor

rokups commented Oct 1, 2019

we are lacking a "reorder items in a list" demo (and eventually "in a tree")

I implemented exactly this in my engine editor by adding extra few pixel high drop areas between list items. For this however i had to modify imgui to have smaller drop area border margins. I should make a PR for this margin to be configurable via style..

@ocornut
Copy link
Owner

ocornut commented Oct 1, 2019

we are lacking a "reorder items in a list" demo (and eventually "in a tree")

I implemented exactly this in my engine editor by adding extra few pixel high drop areas between list items. For this however i had to modify imgui to have smaller drop area border margins. I should make a PR for this margin to be configurable via style..

PR of a small demo would be helpful, I can think of two ways to do it:

  • immediate reordering like how it looks in Drag'n drop support #143 (comment) which is relatively easy to get done
  • reordering with proper drag payload which as you said may need in-between elements (and for trees both "over" and "between" would be valid): we need to explore those and maybe see if/how imgui can provide facilities to avoid having to submit dummy items everywhere.

Feel free to open an issue/PR to discuss this further.

rokups added a commit to rokups/imgui that referenced this issue Oct 2, 2019
ocornut added a commit that referenced this issue Apr 15, 2020
…ither _OpenOnDoubleClick or _OpenOnArrow would open the node. (#143)
ocornut added a commit that referenced this issue Nov 10, 2021
…lowNullID doesn't lose tooltip when scrolling. (#143)

Reduced amount of self critical commentary since it'll appear like a hack for users but it isn't more a hack than many other things.
actondev pushed a commit to actondev/imgui that referenced this issue Nov 26, 2021
…lowNullID doesn't lose tooltip when scrolling. (ocornut#143)

Reduced amount of self critical commentary since it'll appear like a hack for users but it isn't more a hack than many other things.
ocornut added a commit that referenced this issue Nov 23, 2022
…before payload is submitted. (#5910, #143)

+ Added test "widgets_dragdrop_new_payloads" in Test Suite.
kjblanchard pushed a commit to kjblanchard/imgui that referenced this issue May 5, 2023
…before payload is submitted. (ocornut#5910, ocornut#143)

+ Added test "widgets_dragdrop_new_payloads" in Test Suite.
ocornut added a commit that referenced this issue Jun 20, 2024
ocornut added a commit that referenced this issue Jun 20, 2024
…Payload to ImGuiDragDropFlags_PayloadAutoExpire. (#1725, #143)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests