From e6926d9f7c6febb64e9c8458fb8e1c0de321d576 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 13:34:41 +0100 Subject: [PATCH 1/3] Internals: Refactored bool LastItemRectHovered into flags. (#1597) --- imgui.cpp | 17 +++++++++-------- imgui_internal.h | 24 ++++++++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 20ee693e..6b107afd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2035,13 +2035,14 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id) const bool is_clipped = IsClippedEx(bb, id, false); window->DC.LastItemId = id; window->DC.LastItemRect = bb; - window->DC.LastItemRectHoveredRect = false; + window->DC.LastItemStatusFlags = 0; if (is_clipped) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) - window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(bb.Min, bb.Max); + if (IsMouseHoveringRect(bb.Min, bb.Max)) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; } @@ -2054,7 +2055,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiWindow* window = g.CurrentWindow; // Test for bounding box overlap, as updated as ItemAdd() - if (!window->DC.LastItemRectHoveredRect) + if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function @@ -4970,8 +4971,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // After Begin() we fill the last item / hovered data using the title bar data. Make that a standard behavior (to allow usage of context menus on title bar only, etc.). window->DC.LastItemId = window->MoveId; + window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; window->DC.LastItemRect = title_bar_rect; - window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false); } // Inner clipping rectangle @@ -10283,7 +10284,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag EndGroup(); // Drag and Drop Target - if (window->DC.LastItemRectHoveredRect && BeginDragDropTarget()) // NB: The LastItemRectHoveredRect test is merely an optional micro-optimization + if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && BeginDragDropTarget()) // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. { if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) { @@ -11337,7 +11338,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags, int mouse_button) // 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. // 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; + bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0; if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window)) return false; source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); @@ -11388,7 +11389,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags, int mouse_button) } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - window->DC.LastItemRectHoveredRect = false; + window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; return true; } @@ -11485,7 +11486,7 @@ bool ImGui::BeginDragDropTarget() return false; ImGuiWindow* window = g.CurrentWindow; - if (!window->DC.LastItemRectHoveredRect) + if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) return false; diff --git a/imgui_internal.h b/imgui_internal.h index f904cbcf..e80c5297 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -45,6 +45,7 @@ struct ImGuiWindowSettings; typedef int ImGuiLayoutType; // enum: horizontal or vertical // enum ImGuiLayoutType_ typedef int ImGuiButtonFlags; // flags: for ButtonEx(), ButtonBehavior() // enum ImGuiButtonFlags_ typedef int ImGuiItemFlags; // flags: for PushItemFlag() // enum ImGuiItemFlags_ +typedef int ImGuiItemStatusFlags; // flags: storage for DC.LastItemXXX // enum ImGuiItemStatusFlags_ typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ @@ -216,6 +217,12 @@ enum ImGuiSeparatorFlags_ ImGuiSeparatorFlags_Vertical = 1 << 1 }; +// Storage for LastItem data +enum ImGuiItemStatusFlags_ +{ + ImGuiItemStatusFlags_HoveredRect = 1 << 0 +}; + // FIXME: this is in development, not exposed/functional as a generic feature yet. enum ImGuiLayoutType_ { @@ -673,6 +680,7 @@ struct ImGuiContext }; // Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). +// This is going to be exposed in imgui.h when stabilized enough. enum ImGuiItemFlags_ { ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true @@ -699,8 +707,8 @@ struct IMGUI_API ImGuiDrawContext float LogLinePosY; int TreeDepth; ImGuiID LastItemId; - ImRect LastItemRect; - bool LastItemRectHoveredRect; + ImGuiItemStatusFlags LastItemStatusFlags; + ImRect LastItemRect; // Interaction rect bool MenuBarAppending; float MenuBarOffsetX; ImVector ChildWindows; @@ -730,8 +738,8 @@ struct IMGUI_API ImGuiDrawContext LogLinePosY = -1.0f; TreeDepth = 0; LastItemId = 0; + LastItemStatusFlags = 0; LastItemRect = ImRect(); - LastItemRectHoveredRect = false; MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; @@ -838,13 +846,13 @@ public: // Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. struct ImGuiItemHoveredDataBackup { - ImGuiID LastItemId; - ImRect LastItemRect; - bool LastItemRectHoveredRect; + ImGuiID LastItemId; + ImGuiItemStatusFlags LastItemFlags; + ImRect LastItemRect; ImGuiItemHoveredDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemRect = window->DC.LastItemRect; LastItemRectHoveredRect = window->DC.LastItemRectHoveredRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemRect = LastItemRect; window->DC.LastItemRectHoveredRect = LastItemRectHoveredRect; } + void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; } + void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemFlags; window->DC.LastItemRect = LastItemRect; } }; //----------------------------------------------------------------------------- From c6ba7c8231e20ea225ba466400f254b01aca7832 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 13:41:46 +0100 Subject: [PATCH 2/3] Internals: TreeNode: Renaming local to be more consistent with other widgets. --- imgui.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6b107afd..a2db61cc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6741,12 +6741,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // We vertically grow up to current line height up the typical widget height. const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); - ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); + ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); if (display_frame) { // Framed header expand a little outside the default padding - bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; - bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; + frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; + frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; } const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing @@ -6755,7 +6755,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) - const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y); + const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); bool is_open = TreeNodeBehaviorIsOpen(id, flags); if (!ItemAdd(interact_bb, id)) { @@ -6796,36 +6796,36 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, text_base_offset_y); + const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); if (display_frame) { // Framed type - RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - RenderTriangle(bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); + RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); + RenderTriangle(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); if (g.LogEnabled) { // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. const char log_prefix[] = "\n##"; const char log_suffix[] = "##"; LogRenderedText(&text_pos, log_prefix, log_prefix+3); - RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); } else { - RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); } } else { // Unframed typed for tree nodes if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) - RenderFrame(bb.Min, bb.Max, col, false); + RenderFrame(frame_bb.Min, frame_bb.Max, col, false); if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); + RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); else if (!(flags & ImGuiTreeNodeFlags_Leaf)) - RenderTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); + RenderTriangle(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); if (g.LogEnabled) LogRenderedText(&text_pos, ">"); RenderText(text_pos, label, label_end, false); From b50dce54ea0afb7ccc3be479abaeb96976bcdc69 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 13:46:14 +0100 Subject: [PATCH 3/3] Drag and Drop: TreeNode as drop target displays rectangle over full frame. Added optional internal storage for item display rect. Will expose later. (#1597, #143) --- imgui.cpp | 17 +++++++++++------ imgui_internal.h | 13 ++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a2db61cc..4312ed18 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6757,7 +6757,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); bool is_open = TreeNodeBehaviorIsOpen(id, flags); - if (!ItemAdd(interact_bb, id)) + bool item_add = ItemAdd(interact_bb, id); + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + window->DC.LastItemDisplayRect = frame_bb; + + if (!item_add) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushRawID(id); @@ -9537,7 +9541,7 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - window->DC.LastItemRect = bb; + window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. BeginGroup(); if (label_size.x > 0) @@ -10703,9 +10707,9 @@ bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float #ifdef IMGUI_HAS_NAV window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; #endif - bool add = ItemAdd(bb, id); + bool item_add = ItemAdd(bb, id); window->DC.ItemFlags = item_flags_backup; - if (!add) + if (!item_add) return false; bool hovered, held; @@ -11491,13 +11495,14 @@ bool ImGui::BeginDragDropTarget() if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) return false; + const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; ImGuiID id = window->DC.LastItemId; if (id == 0) - id = window->GetIDFromRectangle(window->DC.LastItemRect); + id = window->GetIDFromRectangle(display_rect); if (g.DragDropPayload.SourceId == id) return false; - g.DragDropTargetRect = window->DC.LastItemRect; + g.DragDropTargetRect = display_rect; g.DragDropTargetId = id; return true; } diff --git a/imgui_internal.h b/imgui_internal.h index e80c5297..50046772 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -220,7 +220,8 @@ enum ImGuiSeparatorFlags_ // Storage for LastItem data enum ImGuiItemStatusFlags_ { - ImGuiItemStatusFlags_HoveredRect = 1 << 0 + ImGuiItemStatusFlags_HoveredRect = 1 << 0, + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1 }; // FIXME: this is in development, not exposed/functional as a generic feature yet. @@ -709,6 +710,7 @@ struct IMGUI_API ImGuiDrawContext ImGuiID LastItemId; ImGuiItemStatusFlags LastItemStatusFlags; ImRect LastItemRect; // Interaction rect + ImRect LastItemDisplayRect; // End-user display rect (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) bool MenuBarAppending; float MenuBarOffsetX; ImVector ChildWindows; @@ -739,7 +741,7 @@ struct IMGUI_API ImGuiDrawContext TreeDepth = 0; LastItemId = 0; LastItemStatusFlags = 0; - LastItemRect = ImRect(); + LastItemRect = LastItemDisplayRect = ImRect(); MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; @@ -847,12 +849,13 @@ public: struct ImGuiItemHoveredDataBackup { ImGuiID LastItemId; - ImGuiItemStatusFlags LastItemFlags; + ImGuiItemStatusFlags LastItemStatusFlags; ImRect LastItemRect; + ImRect LastItemDisplayRect; ImGuiItemHoveredDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemFlags; window->DC.LastItemRect = LastItemRect; } + void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } + void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } }; //-----------------------------------------------------------------------------