|
|
@@ -661,7 +661,6 @@ static ImRect GetVisibleRect();
|
|
|
static void CloseInactivePopups(ImGuiWindow* ref_window);
|
|
|
static void ClosePopupToLevel(int remaining);
|
|
|
static ImGuiWindow* GetFrontMostModalRootWindow();
|
|
|
-static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& rect_to_avoid);
|
|
|
|
|
|
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
|
|
|
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
|
|
|
@@ -2031,26 +2030,35 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id)
|
|
|
bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
-
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
+
|
|
|
+ // Test for bounding box overlap, as updated as ItemAdd()
|
|
|
if (!window->DC.LastItemRectHoveredRect)
|
|
|
return false;
|
|
|
IM_ASSERT((flags & ImGuiHoveredFlags_FlattenChilds) == 0); // Flags not supported by this function
|
|
|
|
|
|
+ // Test if we are hovering the right window (our window could be behind another window)
|
|
|
// [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
|
|
|
// Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
|
|
|
//if (g.HoveredWindow != window)
|
|
|
// return false;
|
|
|
if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
|
|
|
return false;
|
|
|
+
|
|
|
+ // Test if another item is active (e.g. being dragged)
|
|
|
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
|
|
|
if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
|
|
|
return false;
|
|
|
+
|
|
|
+ // Test if interactions on this window are blocked by an active popup or modal
|
|
|
if (!IsWindowContentHoverable(window, flags))
|
|
|
return false;
|
|
|
+
|
|
|
+ // Test if the item is disabled
|
|
|
if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
|
|
|
return false;
|
|
|
- // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten.
|
|
|
+
|
|
|
+ // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
|
|
|
if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
|
|
|
return false;
|
|
|
return true;
|
|
|
@@ -2445,9 +2453,10 @@ void ImGui::NewFrame()
|
|
|
}
|
|
|
else if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
|
|
|
{
|
|
|
- // Scroll
|
|
|
- const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
|
|
|
- SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
|
|
|
+ // Mouse wheel Scrolling
|
|
|
+ float scroll_amount = 5 * window->CalcFontSize();
|
|
|
+ scroll_amount = (float)(int)ImMin(scroll_amount, (window->ContentsRegionRect.GetHeight() + window->WindowPadding.y * 2.0f) * 0.67f);
|
|
|
+ SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * scroll_amount);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2764,8 +2773,6 @@ static int ChildWindowComparer(const void* lhs, const void* rhs)
|
|
|
return d;
|
|
|
if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
|
|
|
return d;
|
|
|
- if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
|
|
|
- return d;
|
|
|
return (a->OrderWithinParent - b->OrderWithinParent);
|
|
|
}
|
|
|
|
|
|
@@ -3636,17 +3643,17 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
char window_name[16];
|
|
|
- ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", g.TooltipOverrideCount);
|
|
|
+ ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
|
|
|
if (override_previous_tooltip)
|
|
|
- if (ImGuiWindow* window = ImGui::FindWindowByName(window_name))
|
|
|
+ if (ImGuiWindow* window = FindWindowByName(window_name))
|
|
|
if (window->Active)
|
|
|
{
|
|
|
// Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one.
|
|
|
window->HiddenFrames = 1;
|
|
|
- ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", ++g.TooltipOverrideCount);
|
|
|
+ ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
|
|
|
}
|
|
|
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
|
|
|
- ImGui::Begin(window_name, NULL, flags | extra_flags);
|
|
|
+ Begin(window_name, NULL, flags | extra_flags);
|
|
|
}
|
|
|
|
|
|
void ImGui::SetTooltipV(const char* fmt, va_list args)
|
|
|
@@ -3684,7 +3691,7 @@ void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
int current_stack_size = g.CurrentPopupStack.Size;
|
|
|
- ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
|
|
|
+ ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##Menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
|
|
|
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)
|
|
|
@@ -3798,9 +3805,9 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
|
|
|
|
|
|
char name[20];
|
|
|
if (flags & ImGuiWindowFlags_ChildMenu)
|
|
|
- ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size); // Recycle windows based on depth
|
|
|
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
|
|
|
else
|
|
|
- ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
|
|
|
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
|
|
|
|
|
|
bool is_open = Begin(name, NULL, flags);
|
|
|
if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
|
|
|
@@ -3925,7 +3932,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
|
|
|
|
|
|
const ImVec2 content_avail = ImGui::GetContentRegionAvail();
|
|
|
ImVec2 size = ImFloor(size_arg);
|
|
|
- const int auto_fit_axises = ((size.x == 0.0f) ? 0x01 : 0x00) | ((size.y == 0.0f) ? 0x02 : 0x00);
|
|
|
+ const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
|
|
|
if (size.x <= 0.0f)
|
|
|
size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues)
|
|
|
if (size.y <= 0.0f)
|
|
|
@@ -3967,7 +3974,7 @@ void ImGui::EndChild()
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
|
|
|
IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
|
|
|
- if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
|
|
|
+ if (window->BeginCount > 1)
|
|
|
{
|
|
|
ImGui::End();
|
|
|
}
|
|
|
@@ -3975,9 +3982,9 @@ void ImGui::EndChild()
|
|
|
{
|
|
|
// When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting.
|
|
|
ImVec2 sz = GetWindowSize();
|
|
|
- if (window->AutoFitChildAxises & 0x01) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
|
|
|
+ if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
|
|
|
sz.x = ImMax(4.0f, sz.x);
|
|
|
- if (window->AutoFitChildAxises & 0x02)
|
|
|
+ if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
|
|
|
sz.y = ImMax(4.0f, sz.y);
|
|
|
ImGui::End();
|
|
|
|
|
|
@@ -4022,7 +4029,13 @@ static void CheckStacksSize(ImGuiWindow* window, bool write)
|
|
|
IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
|
|
|
}
|
|
|
|
|
|
-static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid)
|
|
|
+enum ImGuiPopupPositionPolicy
|
|
|
+{
|
|
|
+ ImGuiPopupPositionPolicy_Default,
|
|
|
+ ImGuiPopupPositionPolicy_ComboBox
|
|
|
+};
|
|
|
+
|
|
|
+static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default)
|
|
|
{
|
|
|
const ImGuiStyle& style = GImGui->Style;
|
|
|
|
|
|
@@ -4031,21 +4044,42 @@ static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size,
|
|
|
ImVec2 safe_padding = style.DisplaySafeAreaPadding;
|
|
|
ImRect r_outer(GetVisibleRect());
|
|
|
r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? -safe_padding.y : 0.0f));
|
|
|
- ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
|
|
|
+ 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];
|
|
|
- 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);
|
|
|
- if (avail_w < size.x)
|
|
|
+ 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_h < size.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;
|
|
|
@@ -4055,7 +4089,7 @@ static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size,
|
|
|
|
|
|
// Fallback, try to keep within display
|
|
|
*last_dir = ImGuiDir_None;
|
|
|
- ImVec2 pos = base_pos;
|
|
|
+ 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;
|
|
|
@@ -4126,7 +4160,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
|
|
|
return window;
|
|
|
}
|
|
|
|
|
|
-static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
|
|
|
+static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
if (g.SetNextWindowSizeConstraint)
|
|
|
@@ -4146,6 +4180,8 @@ static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
|
|
|
new_size = data.DesiredSize;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // Minimum size
|
|
|
if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
|
|
|
{
|
|
|
new_size = ImMax(new_size, g.Style.WindowMinSize);
|
|
|
@@ -4154,7 +4190,15 @@ static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
|
|
|
return new_size;
|
|
|
}
|
|
|
|
|
|
-static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
|
|
|
+static ImVec2 CalcSizeContents(ImGuiWindow* window)
|
|
|
+{
|
|
|
+ ImVec2 sz;
|
|
|
+ sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
|
|
|
+ sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
|
|
|
+ return sz + window->WindowPadding;
|
|
|
+}
|
|
|
+
|
|
|
+static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiStyle& style = g.Style;
|
|
|
@@ -4163,16 +4207,16 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
|
|
|
if ((flags & ImGuiWindowFlags_Tooltip) != 0)
|
|
|
{
|
|
|
// Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
|
|
|
- size_auto_fit = window->SizeContents;
|
|
|
+ size_auto_fit = size_contents;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // Handling case of auto fit window not fitting on the screen (on either axis): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
|
|
|
- size_auto_fit = ImClamp(window->SizeContents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
|
|
|
- ImVec2 size_auto_fit_after_constraint = CalcSizeFullWithConstraint(window, size_auto_fit);
|
|
|
- if (size_auto_fit_after_constraint.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
|
|
|
+ // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
|
|
|
+ size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
|
|
|
+ ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
|
|
|
+ if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
|
|
|
size_auto_fit.y += style.ScrollbarSize;
|
|
|
- if (size_auto_fit_after_constraint.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
|
|
|
+ if (size_auto_fit_after_constraint.y < size_contents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
|
|
|
size_auto_fit.x += style.ScrollbarSize;
|
|
|
}
|
|
|
return size_auto_fit;
|
|
|
@@ -4220,7 +4264,7 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co
|
|
|
ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
|
|
|
ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
|
|
|
ImVec2 size_expected = pos_max - pos_min;
|
|
|
- ImVec2 size_constrained = CalcSizeFullWithConstraint(window, size_expected);
|
|
|
+ ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
|
|
|
*out_pos = pos_min;
|
|
|
if (corner_norm.x == 0.0f)
|
|
|
out_pos->x -= (size_constrained.x - size_expected.x);
|
|
|
@@ -4275,13 +4319,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
|
|
|
|
|
// Find or create
|
|
|
- bool window_is_new = false;
|
|
|
ImGuiWindow* window = FindWindowByName(name);
|
|
|
if (!window)
|
|
|
{
|
|
|
ImVec2 size_on_first_use = (g.SetNextWindowSizeCond != 0) ? g.SetNextWindowSizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
|
|
|
window = CreateNewWindow(name, size_on_first_use, flags);
|
|
|
- window_is_new = true;
|
|
|
}
|
|
|
|
|
|
const int current_frame = g.FrameCount;
|
|
|
@@ -4322,7 +4364,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
}
|
|
|
|
|
|
// Process SetNextWindow***() calls
|
|
|
- bool window_pos_set_by_api = false, window_size_set_by_api = false;
|
|
|
+ bool window_pos_set_by_api = false;
|
|
|
+ bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
|
|
|
if (g.SetNextWindowPosCond)
|
|
|
{
|
|
|
window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
|
|
|
@@ -4342,7 +4385,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
}
|
|
|
if (g.SetNextWindowSizeCond)
|
|
|
{
|
|
|
- window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
|
|
|
+ window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0 && (g.SetNextWindowSizeVal.x > 0.0f);
|
|
|
+ window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0 && (g.SetNextWindowSizeVal.y > 0.0f);
|
|
|
SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
|
|
|
g.SetNextWindowSizeCond = 0;
|
|
|
}
|
|
|
@@ -4390,7 +4434,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
window->DrawList->Clear();
|
|
|
window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
|
|
|
ImRect fullscreen_rect(GetVisibleRect());
|
|
|
- if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
|
|
|
+ if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
|
|
|
PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
|
|
|
else
|
|
|
PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
|
|
|
@@ -4423,11 +4467,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
// SIZE
|
|
|
|
|
|
// Update contents size from last frame for auto-fitting (unless explicitly specified)
|
|
|
- window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x));
|
|
|
- window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y));
|
|
|
- window->SizeContents += window->WindowPadding;
|
|
|
+ window->SizeContents = CalcSizeContents(window);
|
|
|
|
|
|
- // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
|
|
|
+ // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
|
|
|
if (window->HiddenFrames > 0)
|
|
|
window->HiddenFrames--;
|
|
|
if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user)
|
|
|
@@ -4435,8 +4477,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
window->HiddenFrames = 1;
|
|
|
if (flags & ImGuiWindowFlags_AlwaysAutoResize)
|
|
|
{
|
|
|
- if (!window_size_set_by_api)
|
|
|
- window->Size = window->SizeFull = ImVec2(0.f, 0.f);
|
|
|
+ if (!window_size_x_set_by_api)
|
|
|
+ window->Size.x = window->SizeFull.x = 0.f;
|
|
|
+ if (!window_size_y_set_by_api)
|
|
|
+ window->Size.y = window->SizeFull.y = 0.f;
|
|
|
window->SizeContents = ImVec2(0.f, 0.f);
|
|
|
}
|
|
|
}
|
|
|
@@ -4445,46 +4489,40 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
|
|
|
window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
|
|
|
window->WindowPadding = style.WindowPadding;
|
|
|
- if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
|
|
|
+ if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
|
|
|
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
|
|
|
const float window_rounding = window->WindowRounding;
|
|
|
const float window_border_size = window->WindowBorderSize;
|
|
|
|
|
|
// Calculate auto-fit size, handle automatic resize
|
|
|
- const ImVec2 size_auto_fit = CalcSizeAutoFit(window);
|
|
|
- ImVec2 size_for_scrollbars_visibility = window->SizeFullAtLastBegin;
|
|
|
- if (window->Collapsed)
|
|
|
+ const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
|
|
|
+ ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
|
|
|
+ if (flags & ImGuiWindowFlags_AlwaysAutoResize && !window->Collapsed)
|
|
|
{
|
|
|
- // We still process initial auto-fit on collapsed windows to get a window width,
|
|
|
- // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
|
|
|
- if (window->AutoFitFramesX > 0)
|
|
|
- window->SizeFull.x = size_for_scrollbars_visibility.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
|
|
|
- if (window->AutoFitFramesY > 0)
|
|
|
- window->SizeFull.y = size_for_scrollbars_visibility.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
|
|
|
+ // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
|
|
|
+ if (!window_size_x_set_by_api)
|
|
|
+ window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
|
|
|
+ if (!window_size_y_set_by_api)
|
|
|
+ window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
|
|
|
}
|
|
|
- else if (!window_size_set_by_api)
|
|
|
+ else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
|
|
|
{
|
|
|
- if (flags & ImGuiWindowFlags_AlwaysAutoResize)
|
|
|
- {
|
|
|
- window->SizeFull = size_for_scrollbars_visibility = size_auto_fit;
|
|
|
- }
|
|
|
- else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
|
|
|
- {
|
|
|
- // Auto-fit only grows during the first few frames
|
|
|
- if (window->AutoFitFramesX > 0)
|
|
|
- window->SizeFull.x = size_for_scrollbars_visibility.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
|
|
|
- if (window->AutoFitFramesY > 0)
|
|
|
- window->SizeFull.y = size_for_scrollbars_visibility.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
|
|
|
+ // Auto-fit only grows during the first few frames
|
|
|
+ // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
|
|
|
+ if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
|
|
|
+ window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
|
|
|
+ if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
|
|
|
+ window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
|
|
|
+ if (!window->Collapsed)
|
|
|
MarkIniSettingsDirty(window);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
// Apply minimum/maximum window size constraints and final size
|
|
|
- window->SizeFull = CalcSizeFullWithConstraint(window, window->SizeFull);
|
|
|
+ window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
|
|
|
window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
|
|
|
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
|
|
|
{
|
|
|
- IM_ASSERT(window_size_set_by_api); // Submitted by BeginChild()
|
|
|
+ IM_ASSERT(window_size_x_set_by_api && window_size_x_set_by_api); // Submitted by BeginChild()
|
|
|
window->Size = window->SizeFull;
|
|
|
}
|
|
|
|
|
|
@@ -4493,10 +4531,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
// Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
|
|
|
if (!window->Collapsed)
|
|
|
{
|
|
|
- window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_for_scrollbars_visibility.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
|
|
|
- window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_for_scrollbars_visibility.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
|
|
|
+ // When reading the current size we need to read it after size constraints have been applied
|
|
|
+ float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
|
|
|
+ float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
|
|
|
+ window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
|
|
|
+ window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
|
|
|
if (window->ScrollbarX && !window->ScrollbarY)
|
|
|
- window->ScrollbarY = (window->SizeContents.y > size_for_scrollbars_visibility.y + style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
|
|
|
+ window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars + style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
|
|
|
window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
|
|
|
}
|
|
|
|
|
|
@@ -4528,12 +4569,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
|
|
|
else
|
|
|
rect_to_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);
|
|
|
- window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
|
|
|
+ window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
|
|
|
}
|
|
|
else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
|
|
|
{
|
|
|
ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
|
|
|
- window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
|
|
|
+ window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
|
|
|
}
|
|
|
|
|
|
// Position tooltip (always follows mouse)
|
|
|
@@ -4541,7 +4582,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
{
|
|
|
ImVec2 ref_pos = g.IO.MousePos;
|
|
|
ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Store boxes in mouse cursor data? Scale? Center on cursor hit-point?
|
|
|
- window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
|
|
|
+ window->PosFloat = FindBestWindowPosForPopup(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
|
|
|
if (window->AutoPosLastDirection == ImGuiDir_None)
|
|
|
window->PosFloat = 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.
|
|
|
}
|
|
|
@@ -4627,7 +4668,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
|
|
|
{
|
|
|
// Manual auto-fit when double-clicking
|
|
|
- size_target = CalcSizeFullWithConstraint(window, size_auto_fit);
|
|
|
+ size_target = CalcSizeAfterConstraint(window, size_auto_fit);
|
|
|
ClearActiveID();
|
|
|
}
|
|
|
else if (held)
|
|
|
@@ -9030,62 +9071,25 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla
|
|
|
return InputIntN(label, v, 4, extra_flags);
|
|
|
}
|
|
|
|
|
|
-static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
|
|
|
-{
|
|
|
- const char* const* items = (const char* const*)data;
|
|
|
- if (out_text)
|
|
|
- *out_text = items[idx];
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
|
|
|
-{
|
|
|
- // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
|
|
|
- const char* items_separated_by_zeros = (const char*)data;
|
|
|
- int items_count = 0;
|
|
|
- const char* p = items_separated_by_zeros;
|
|
|
- while (*p)
|
|
|
- {
|
|
|
- if (idx == items_count)
|
|
|
- break;
|
|
|
- p += strlen(p) + 1;
|
|
|
- items_count++;
|
|
|
- }
|
|
|
- if (!*p)
|
|
|
- return false;
|
|
|
- if (out_text)
|
|
|
- *out_text = p;
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-// Combo box helper allowing to pass an array of strings.
|
|
|
-bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
|
|
|
+static float CalcMaxPopupHeightFromItemCount(int items_count)
|
|
|
{
|
|
|
- const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
|
|
|
- return value_changed;
|
|
|
-}
|
|
|
-
|
|
|
-// Combo box helper allowing to pass all items in a single string.
|
|
|
-bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
|
|
|
-{
|
|
|
- int items_count = 0;
|
|
|
- const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
|
|
|
- while (*p)
|
|
|
- {
|
|
|
- p += strlen(p) + 1;
|
|
|
- items_count++;
|
|
|
- }
|
|
|
- bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
|
|
|
- return value_changed;
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ if (items_count <= 0)
|
|
|
+ return FLT_MAX;
|
|
|
+ return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
|
|
|
}
|
|
|
|
|
|
-bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popup_size)
|
|
|
+bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
|
|
|
{
|
|
|
+ // Always consume the SetNextWindowSizeConstraint() call in our early return paths
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ bool backup_has_next_window_size_constraint = g.SetNextWindowSizeConstraint;
|
|
|
+ g.SetNextWindowSizeConstraint = false;
|
|
|
+
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
if (window->SkipItems)
|
|
|
return false;
|
|
|
|
|
|
- ImGuiContext& g = *GImGui;
|
|
|
const ImGuiStyle& style = g.Style;
|
|
|
const ImGuiID id = window->GetID(label);
|
|
|
const float w = CalcItemWidth();
|
|
|
@@ -9097,21 +9101,17 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popu
|
|
|
if (!ItemAdd(total_bb, id))
|
|
|
return false;
|
|
|
|
|
|
- const float arrow_size = SmallSquareSize();
|
|
|
-
|
|
|
bool hovered, held;
|
|
|
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
|
|
|
-
|
|
|
bool popup_open = IsPopupOpen(id);
|
|
|
|
|
|
+ const float arrow_size = SmallSquareSize();
|
|
|
const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
|
|
|
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
|
|
|
RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
|
|
|
RenderTriangle(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
|
|
|
-
|
|
|
if (preview_value != NULL)
|
|
|
RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
|
|
|
-
|
|
|
if (label_size.x > 0)
|
|
|
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
|
|
|
|
|
@@ -9124,58 +9124,75 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popu
|
|
|
if (!popup_open)
|
|
|
return false;
|
|
|
|
|
|
- if (popup_size.x == 0.0f)
|
|
|
- popup_size.x = w;
|
|
|
-
|
|
|
- float popup_y1 = frame_bb.Max.y;
|
|
|
- float popup_y2 = ImClamp(popup_y1 + popup_size.y, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
|
|
|
- if ((popup_y2 - popup_y1) < ImMin(popup_size.y, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
|
|
|
+ if (backup_has_next_window_size_constraint)
|
|
|
{
|
|
|
- // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
|
|
|
- popup_y1 = ImClamp(frame_bb.Min.y - popup_size.y, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
|
|
|
- popup_y2 = frame_bb.Min.y;
|
|
|
- SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FrameBorderSize), ImGuiCond_Always, ImVec2(0.0f, 1.0f));
|
|
|
+ g.SetNextWindowSizeConstraint = true;
|
|
|
+ g.SetNextWindowSizeConstraintRect.Min.x = ImMax(g.SetNextWindowSizeConstraintRect.Min.x, w);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // Position our combo below
|
|
|
- SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Max.y - style.FrameBorderSize), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
|
|
|
+ if ((flags & ImGuiComboFlags_HeightMask_) == 0)
|
|
|
+ flags |= ImGuiComboFlags_HeightRegular;
|
|
|
+ IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one
|
|
|
+ int popup_max_height_in_items = -1;
|
|
|
+ if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8;
|
|
|
+ else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4;
|
|
|
+ else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20;
|
|
|
+ SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
|
|
|
}
|
|
|
- SetNextWindowSize(ImVec2(popup_size.x, popup_y2 - popup_y1), ImGuiCond_Appearing);
|
|
|
- PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
|
|
|
|
|
|
- if (!BeginPopupEx(id, ImGuiWindowFlags_ComboBox))
|
|
|
+ char name[16];
|
|
|
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
|
|
|
+
|
|
|
+ // Peak into expected window size so we can position it
|
|
|
+ if (ImGuiWindow* popup_window = FindWindowByName(name))
|
|
|
{
|
|
|
+ ImVec2 size_contents = CalcSizeContents(popup_window);
|
|
|
+ ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents));
|
|
|
+ if (flags & ImGuiComboFlags_PopupAlignLeft)
|
|
|
+ popup_window->AutoPosLastDirection = ImGuiDir_Left;
|
|
|
+ ImVec2 pos = FindBestWindowPosForPopup(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
|
|
|
+ SetNextWindowPos(pos);
|
|
|
+ }
|
|
|
+
|
|
|
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
|
|
|
+ if (!Begin(name, NULL, window_flags))
|
|
|
+ {
|
|
|
+ EndPopup();
|
|
|
IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
|
|
|
return false;
|
|
|
}
|
|
|
- Spacing();
|
|
|
+
|
|
|
+ // Horizontally align ourselves with the framed text
|
|
|
+ if (style.FramePadding.x != style.WindowPadding.x)
|
|
|
+ Indent(style.FramePadding.x - style.WindowPadding.x);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void ImGui::EndCombo()
|
|
|
{
|
|
|
+ const ImGuiStyle& style = GImGui->Style;
|
|
|
+ if (style.FramePadding.x != style.WindowPadding.x)
|
|
|
+ Unindent(style.FramePadding.x - style.WindowPadding.x);
|
|
|
EndPopup();
|
|
|
- PopStyleVar();
|
|
|
}
|
|
|
|
|
|
// Combo box function.
|
|
|
-bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
|
|
|
+bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
- const ImGuiStyle& style = g.Style;
|
|
|
|
|
|
const char* preview_text = NULL;
|
|
|
if (*current_item >= 0 && *current_item < items_count)
|
|
|
items_getter(data, *current_item, &preview_text);
|
|
|
|
|
|
- // Size default to hold ~7 items
|
|
|
- if (height_in_items < 0)
|
|
|
- height_in_items = 7;
|
|
|
- float popup_height = (g.FontSize + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
|
|
|
-
|
|
|
- if (!BeginCombo(label, preview_text, ImVec2(0.0f, popup_height)))
|
|
|
+ if (popup_max_height_in_items != -1 && !g.SetNextWindowSizeConstraint)
|
|
|
+ {
|
|
|
+ float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
|
|
|
+ SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, popup_max_height));
|
|
|
+ }
|
|
|
+ if (!BeginCombo(label, preview_text, 0))
|
|
|
return false;
|
|
|
|
|
|
// Display items
|
|
|
@@ -9202,6 +9219,55 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
|
|
|
return value_changed;
|
|
|
}
|
|
|
|
|
|
+static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
|
|
|
+{
|
|
|
+ const char* const* items = (const char* const*)data;
|
|
|
+ if (out_text)
|
|
|
+ *out_text = items[idx];
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
|
|
|
+{
|
|
|
+ // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
|
|
|
+ const char* items_separated_by_zeros = (const char*)data;
|
|
|
+ int items_count = 0;
|
|
|
+ const char* p = items_separated_by_zeros;
|
|
|
+ while (*p)
|
|
|
+ {
|
|
|
+ if (idx == items_count)
|
|
|
+ break;
|
|
|
+ p += strlen(p) + 1;
|
|
|
+ items_count++;
|
|
|
+ }
|
|
|
+ if (!*p)
|
|
|
+ return false;
|
|
|
+ if (out_text)
|
|
|
+ *out_text = p;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// Combo box helper allowing to pass an array of strings.
|
|
|
+bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
|
|
|
+{
|
|
|
+ const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
|
|
|
+ return value_changed;
|
|
|
+}
|
|
|
+
|
|
|
+// Combo box helper allowing to pass all items in a single string.
|
|
|
+bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
|
|
|
+{
|
|
|
+ int items_count = 0;
|
|
|
+ const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
|
|
|
+ while (*p)
|
|
|
+ {
|
|
|
+ p += strlen(p) + 1;
|
|
|
+ items_count++;
|
|
|
+ }
|
|
|
+ bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
|
|
|
+ return value_changed;
|
|
|
+}
|
|
|
+
|
|
|
// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
|
|
|
// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
|
|
|
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
|
|
|
@@ -9520,7 +9586,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].ParentMenuSet == window->GetID("##Menus"));
|
|
|
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)
|
|
|
@@ -10900,7 +10966,7 @@ void ImGui::Indent(float indent_w)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
- window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
|
|
|
+ window->DC.IndentX += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
|
|
|
window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
|
|
|
}
|
|
|
|
|
|
@@ -10908,7 +10974,7 @@ void ImGui::Unindent(float indent_w)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
- window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
|
|
|
+ window->DC.IndentX -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
|
|
|
window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
|
|
|
}
|
|
|
|