From 0e775807b4be21405817df60c53889ab0a1ce230 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 29 Oct 2017 23:10:44 +0100 Subject: [PATCH] Drag and Drop: Added a mechanism to allow widgets with no identifiers (such as Text/Image) to be used with BeginDragDropSource() given the explicit ImGuiDragDropFlags_SourceAllowNullID flag. --- imgui.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++-- imgui.h | 1 + imgui_internal.h | 1 + 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b11a28d73..de89f4f69 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1860,6 +1860,16 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); } +// This is particularly dodgy and used in extremely rare situation. +ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) +{ + ImGuiID seed = IDStack.back(); + const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; + ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed); + ImGui::KeepAliveID(id); + return id; +} + //----------------------------------------------------------------------------- // Internal API exposed in imgui_internal.h //----------------------------------------------------------------------------- @@ -10606,16 +10616,48 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags, int mouse_button) ImGuiWindow* window = g.CurrentWindow; if (g.IO.MouseDown[mouse_button] == false) return false; - if (g.ActiveId != window->DC.LastItemId) + + ImGuiID id = window->DC.LastItemId; + if (id == 0) + { + // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: + // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. + if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) + { + IM_ASSERT(0); + return false; + } + + // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image(). + // We build a throwaway ID based on current ID stack + relative AABB of items in window. + // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. + // If you want fail-proof dragging, + // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. + bool is_hovered = window->DC.LastItemRectHoveredRect; + if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window)) + return false; + id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); + if (is_hovered) + SetHoveredID(id); + if (is_hovered && g.IO.MouseClicked[mouse_button]) + { + SetActiveID(id, window); + FocusWindow(window); + } + if (g.ActiveId == id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. + g.ActiveIdAllowOverlap = is_hovered; + } + if (g.ActiveId != id) return false; if (IsMouseDragging(mouse_button)) { if (!g.DragDropActive) { + IM_ASSERT(id != 0); ImGuiPayload& payload = g.DragDropPayload; payload.Clear(); - payload.SourceId = g.ActiveId; + payload.SourceId = id; payload.SourceParentId = window->IDStack.back(); g.DragDropActive = true; g.DragDropSourceFlags = flags; diff --git a/imgui.h b/imgui.h index 7f1baa9c8..25a9c5a6c 100644 --- a/imgui.h +++ b/imgui.h @@ -599,6 +599,7 @@ enum ImGuiDragDropFlags_ // BeginDragDropSource() flags ImGuiDragDropFlags_SourceNoAutoTooltip = 1 << 0, ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return true, to avoid subsequent user code submitting tooltips. + ImGuiDragDropFlags_SourceAllowNullID = 1 << 2, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. // BeginDragDropTarget() flags ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. diff --git a/imgui_internal.h b/imgui_internal.h index 666b4491a..dafb3aef8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -754,6 +754,7 @@ public: ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const void* ptr); ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); + ImGuiID GetIDFromRectangle(const ImRect& r_abs); ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; }