diff --git a/imgui.cpp b/imgui.cpp index 10e44a4e4..d6bb4d02d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4401,7 +4401,7 @@ ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() { ImGuiContext& g = *GImGui; if (g.CurrentPopupStack.Size > 0) - return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen; + return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos; return g.IO.MousePos; } @@ -4584,32 +4584,48 @@ void ImGui::EndTooltip() // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing) +void ImGui::OpenPopupEx(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; int current_stack_size = g.CurrentPopupStack.Size; - ImVec2 mouse_pos = g.IO.MousePos; - ImVec2 popup_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : mouse_pos; - ImGuiPopupRef popup_ref = ImGuiPopupRef(id, parent_window, parent_window->GetID("##Menus"), popup_pos, mouse_pos); // Tagged as new ref because constructor sets Window to NULL. + ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. + popup_ref.PopupId = id; + popup_ref.Window = NULL; + popup_ref.ParentWindow = parent_window; + popup_ref.OpenFrameCount = g.FrameCount; + popup_ref.OpenParentId = parent_window->IDStack.back(); + popup_ref.OpenMousePos = g.IO.MousePos; + popup_ref.OpenPopupPos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; + if (g.OpenPopupStack.Size < current_stack_size + 1) - g.OpenPopupStack.push_back(popup_ref); - else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id) { - g.OpenPopupStack.resize(current_stack_size+1); - g.OpenPopupStack[current_stack_size] = popup_ref; + g.OpenPopupStack.push_back(popup_ref); + } + else + { + // Close child popups if any + g.OpenPopupStack.resize(current_stack_size + 1); + + // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui + // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing + // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. + if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) + g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; + else + g.OpenPopupStack[current_stack_size] = popup_ref; // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by CloseInactivePopups(). // This is equivalent to what ClosePopupToLevel() does. - if (g.OpenPopupStack[current_stack_size].PopupId == id) - FocusWindow(parent_window); + //if (g.OpenPopupStack[current_stack_size].PopupId == id) + // FocusWindow(parent_window); } } void ImGui::OpenPopup(const char* str_id) { ImGuiContext& g = *GImGui; - OpenPopupEx(g.CurrentWindow->GetID(str_id), false); + OpenPopupEx(g.CurrentWindow->GetID(str_id)); } static void CloseInactivePopups(ImGuiWindow* ref_window) @@ -4784,11 +4800,11 @@ void ImGui::EndPopup() bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) { ImGuiWindow* window = GImGui->CurrentWindow; - if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) { ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - OpenPopupEx(id, true); + OpenPopupEx(id); return true; } return false; @@ -4802,9 +4818,8 @@ bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) ImGuiWindow* window = GImGui->CurrentWindow; ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - if (IsMouseClicked(mouse_button)) - if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - OpenPopupEx(id, true); + if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + OpenPopupEx(id); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); } @@ -4813,10 +4828,9 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool a if (!str_id) str_id = "window_context"; ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (IsMouseClicked(mouse_button)) - if (IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - if (also_over_items || !IsAnyItemHovered()) - OpenPopupEx(id, true); + if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + if (also_over_items || !IsAnyItemHovered()) + OpenPopupEx(id); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); } @@ -4825,8 +4839,8 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) if (!str_id) str_id = "void_context"; ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button)) - OpenPopupEx(id, true); + if (IsMouseReleased(mouse_button) && !IsAnyWindowHovered()) + OpenPopupEx(id); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); } @@ -5383,7 +5397,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Popup first latch mouse position, will position itself when it appears next frame window->AutoPosLastDirection = ImGuiDir_None; if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) - window->PosFloat = g.CurrentPopupStack.back().PopupPosOnOpen; + window->PosFloat = g.CurrentPopupStack.back().OpenPopupPos; } // Collapse window by double-clicking on title bar @@ -7249,20 +7263,14 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (!(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); FocusWindow(window); - g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; } if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveID) - { ClearActiveID(); - } else - { SetActiveID(id, window); // Hold on ID - g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; - } FocusWindow(window); } if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) @@ -7309,6 +7317,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { if (g.ActiveIdSource == ImGuiInputSource_Mouse) { + if (g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; if (g.IO.MouseDown[0]) { held = true; @@ -10297,7 +10307,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF { if (window->DC.NavLayerCurrent == 0) window->NavLastIds[0] = id; - OpenPopupEx(id, false); + OpenPopupEx(id); popup_open = true; } @@ -10811,7 +10821,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) bool pressed; bool menu_is_open = IsPopupOpen(id); - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##Menus")); + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back()); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) diff --git a/imgui_internal.h b/imgui_internal.h index 54749a7c3..49aa4b068 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -418,11 +418,10 @@ struct ImGuiPopupRef ImGuiID PopupId; // Set on OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() ImGuiWindow* ParentWindow; // Set on OpenPopup() - ImGuiID ParentMenuSet; // Set on OpenPopup() - ImVec2 PopupPosOnOpen; // Preferred popup position (typically == MousePosOnOpen when using mouse) - ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup - - ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& popup_pos, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; PopupPosOnOpen = popup_pos; MousePosOnOpen = mouse_pos; } + int OpenFrameCount; // Set on OpenPopup() + ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differenciate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) + ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) + ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup }; struct ImGuiColumnData @@ -996,7 +995,7 @@ namespace ImGui IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); - IMGUI_API void OpenPopupEx(ImGuiID id, bool reopen_existing); + IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void ClosePopup(ImGuiID id); IMGUI_API bool IsPopupOpen(ImGuiID id); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);