Refactor: Internals: Moved Popup functions in imgui.cpp in their own section. (part 3) (#2036)
diff --git a/imgui.cpp b/imgui.cpp
index 8cd2dd4..9651e97 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4167,112 +4167,6 @@
IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
}
-ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
-{
- ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
- ImRect r_screen = GetViewportRect();
- r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
- return r_screen;
-}
-
-// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
-// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
-ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
-{
- ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
- //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
- //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
-
- // Combo Box policy (we want a connecting edge)
- if (policy == ImGuiPopupPositionPolicy_ComboBox)
- {
- const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
- for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
- {
- const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
- if (n != -1 && dir == *last_dir) // Already tried this direction?
- continue;
- ImVec2 pos;
- if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
- if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
- if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
- if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
- if (!r_outer.Contains(ImRect(pos, pos + size)))
- continue;
- *last_dir = dir;
- return pos;
- }
- }
-
- // Default popup policy
- const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
- for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
- {
- const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
- if (n != -1 && dir == *last_dir) // Already tried this direction?
- continue;
- float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
- float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
- if (avail_w < size.x || avail_h < size.y)
- continue;
- ImVec2 pos;
- pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
- pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
- *last_dir = dir;
- return pos;
- }
-
- // Fallback, try to keep within display
- *last_dir = ImGuiDir_None;
- ImVec2 pos = ref_pos;
- pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
- pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
- return pos;
-}
-
-ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
-{
- ImGuiContext& g = *GImGui;
-
- ImRect r_outer = GetWindowAllowedExtentRect(window);
- if (window->Flags & ImGuiWindowFlags_ChildMenu)
- {
- // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds.
- // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
- IM_ASSERT(g.CurrentWindow == window);
- ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
- float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
- ImRect r_avoid;
- if (parent_window->DC.MenuBarAppending)
- r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
- else
- r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
- return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
- }
- if (window->Flags & ImGuiWindowFlags_Popup)
- {
- ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
- return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
- }
- if (window->Flags & ImGuiWindowFlags_Tooltip)
- {
- // Position tooltip (always follows mouse)
- float sc = g.Style.MouseCursorScale;
- ImVec2 ref_pos = NavCalcPreferredRefPos();
- ImRect r_avoid;
- if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
- r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
- else
- r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
- ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
- if (window->AutoPosLastDirection == ImGuiDir_None)
- pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
- return pos;
- }
- IM_ASSERT(0);
- return window->Pos;
-}
-
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
{
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
@@ -6766,6 +6660,112 @@
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
}
+ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
+{
+ ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
+ ImRect r_screen = GetViewportRect();
+ r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
+ return r_screen;
+}
+
+// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
+// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
+ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
+{
+ ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
+ //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
+ //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
+
+ // Combo Box policy (we want a connecting edge)
+ if (policy == ImGuiPopupPositionPolicy_ComboBox)
+ {
+ const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
+ for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
+ {
+ const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
+ if (n != -1 && dir == *last_dir) // Already tried this direction?
+ continue;
+ ImVec2 pos;
+ if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
+ if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
+ if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
+ if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
+ if (!r_outer.Contains(ImRect(pos, pos + size)))
+ continue;
+ *last_dir = dir;
+ return pos;
+ }
+ }
+
+ // Default popup policy
+ const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
+ for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
+ {
+ const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
+ if (n != -1 && dir == *last_dir) // Already tried this direction?
+ continue;
+ float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
+ float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
+ if (avail_w < size.x || avail_h < size.y)
+ continue;
+ ImVec2 pos;
+ pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
+ pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
+ *last_dir = dir;
+ return pos;
+ }
+
+ // Fallback, try to keep within display
+ *last_dir = ImGuiDir_None;
+ ImVec2 pos = ref_pos;
+ pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
+ pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
+ return pos;
+}
+
+ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+
+ ImRect r_outer = GetWindowAllowedExtentRect(window);
+ if (window->Flags & ImGuiWindowFlags_ChildMenu)
+ {
+ // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds.
+ // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
+ IM_ASSERT(g.CurrentWindow == window);
+ ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
+ float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
+ ImRect r_avoid;
+ if (parent_window->DC.MenuBarAppending)
+ r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
+ else
+ r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
+ return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
+ }
+ if (window->Flags & ImGuiWindowFlags_Popup)
+ {
+ ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
+ return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
+ }
+ if (window->Flags & ImGuiWindowFlags_Tooltip)
+ {
+ // Position tooltip (always follows mouse)
+ float sc = g.Style.MouseCursorScale;
+ ImVec2 ref_pos = NavCalcPreferredRefPos();
+ ImRect r_avoid;
+ if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
+ r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
+ else
+ r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
+ ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
+ if (window->AutoPosLastDirection == ImGuiDir_None)
+ pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
+ return pos;
+ }
+ IM_ASSERT(0);
+ return window->Pos;
+}
+
//-----------------------------------------------------------------------------
// NAVIGATION
//-----------------------------------------------------------------------------