Merge branch 'master' into docking

# Conflicts:
#	imgui.cpp
#	imgui_internal.h
This commit is contained in:
omar 2019-04-29 13:06:55 +02:00
commit 7e67aba286
7 changed files with 221 additions and 165 deletions

View File

@ -98,6 +98,8 @@ Other changes:
----------------------------------------------------------------------- -----------------------------------------------------------------------
Breaking Changes: Breaking Changes:
- ImDrawList: Fixed rectangles with thick lines (>1.0f) not being as thick as requested. (#2518)
If you have custom rendering using thick lines, they will appear thicker now.
- Examples: Vulkan: Added MinImageCount/ImageCount fields in ImGui_ImplVulkan_InitInfo, required - Examples: Vulkan: Added MinImageCount/ImageCount fields in ImGui_ImplVulkan_InitInfo, required
during initialization to specify the number of in-flight image requested by swap chains. during initialization to specify the number of in-flight image requested by swap chains.
(was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). (#2071, #1677) [@nathanvoglsam] (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). (#2071, #1677) [@nathanvoglsam]
@ -122,13 +124,23 @@ Other Changes:
One of the noticeable minor side effect was that navigating menus would have had a tendency to disable One of the noticeable minor side effect was that navigating menus would have had a tendency to disable
highlight from parent menu items earlier than necessary while approaching the child menu. highlight from parent menu items earlier than necessary while approaching the child menu.
- Window: Close button is horizontally aligned with style.FramePadding.x. - Window: Close button is horizontally aligned with style.FramePadding.x.
- Window: Fixed contents region being off by WindowBorderSize amount on the right when scrollbar is active.
- Popups: Closing a popup restores the focused/nav window in place at the time of the popup opening,
instead of restoring the window that was in the window stack at the time of the OpenPopup call. (#2517)
Among other things, this allows opening a popup while no window are focused, and pressing Escape to
clear the focus again.
- Popups: Fixed right-click from closing all popups instead of aiming at the hovered popup level
(regression in 1.67).
- GetMouseDragDelta(): also returns the delta on the mouse button released frame. (#2419) - GetMouseDragDelta(): also returns the delta on the mouse button released frame. (#2419)
- GetMouseDragDelta(): verify that mouse positions are valid otherwise returns zero. - GetMouseDragDelta(): verify that mouse positions are valid otherwise returns zero.
- Inputs: Also add support for horizontal scroll with Shift+Mouse Wheel. (#2424, #1463) [@LucaRood] - Inputs: Also add support for horizontal scroll with Shift+Mouse Wheel. (#2424, #1463) [@LucaRood]
- PlotLines, PlotHistogram: Ignore NaN values when calculating min/max bounds. (#2485) - PlotLines, PlotHistogram: Ignore NaN values when calculating min/max bounds. (#2485)
- Columns: Fixed boundary of clipping being off by 1 pixel within the left column. - Columns: Fixed boundary of clipping being off by 1 pixel within the left column. (#125)
- Separator: Declare its thickness (1.0f) to the layout, making items around separator more symmetrical.
- Combo, Slider, Scrollbar: Improve rendering in situation when there's only a few pixels available (<3 pixels). - Combo, Slider, Scrollbar: Improve rendering in situation when there's only a few pixels available (<3 pixels).
- Nav: Fixed Drag/Slider functions going into text input mode when keyboard CTRL is held while pressing NavActivate. - Nav: Fixed Drag/Slider functions going into text input mode when keyboard CTRL is held while pressing NavActivate.
- ImDrawList: Improved algorithm for mitre joints on thick lines, preserving correct thickness up to 90 degrees
angles, also faster to output. (#2518) [@rmitton]
- Misc: Added IM_MALLOC/IM_FREE macros mimicking IM_NEW/IM_DELETE so user doesn't need to revert - Misc: Added IM_MALLOC/IM_FREE macros mimicking IM_NEW/IM_DELETE so user doesn't need to revert
to using the ImGui::MemAlloc()/MemFree() calls directly. to using the ImGui::MemAlloc()/MemFree() calls directly.
- Metrics: Added "Show windows rectangles" tool to visualize the different rectangles. - Metrics: Added "Show windows rectangles" tool to visualize the different rectangles.

View File

@ -318,6 +318,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- nav/menus: allow pressing Menu to leave a sub-menu. - nav/menus: allow pressing Menu to leave a sub-menu.
- nav/menus: a way to access the main menu bar with Alt? (currently needs CTRL+TAB) - nav/menus: a way to access the main menu bar with Alt? (currently needs CTRL+TAB)
- nav/menus: when using the main menu bar, even though we restore focus after, the underlying window loses its title bar highlight during menu manipulation. could we prevent it? - nav/menus: when using the main menu bar, even though we restore focus after, the underlying window loses its title bar highlight during menu manipulation. could we prevent it?
- nav/menus: main menu bar currently cannot restore a NULL focus. Could save NavWindow at the time of being focused, similarly to what popup do?
- nav: simulate right-click or context activation? (SHIFT+F10) - nav: simulate right-click or context activation? (SHIFT+F10)
- nav: tabs should go through most/all widgets (in submission order?). - nav: tabs should go through most/all widgets (in submission order?).
- nav: when CTRL-Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering. - nav: when CTRL-Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering.

152
imgui.cpp
View File

@ -380,6 +380,7 @@ CODE
- 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
- 2019/04/29 (1.70) - fixed ImDrawList rectangles with thick lines (>1.0f) not being as thick as requested. If you have custom rendering using rectangles with thick lines, they will appear thicker now.
- 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
- 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
- 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value! - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value!
@ -1081,6 +1082,7 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb
static ImVec2 NavCalcPreferredRefPos(); static ImVec2 NavCalcPreferredRefPos();
static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
static int FindWindowFocusIndex(ImGuiWindow* window);
// Misc // Misc
static void UpdateMouseInputs(); static void UpdateMouseInputs();
@ -2941,7 +2943,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
return false; return false;
// Test if interactions on this window are blocked by an active popup or modal // Test if interactions on this window are blocked by an active popup or modal.
// The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
if (!IsWindowContentHoverable(window, flags)) if (!IsWindowContentHoverable(window, flags))
return false; return false;
@ -3328,8 +3331,9 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
} }
} }
// With right mouse button we close popups without changing focus // With right mouse button we close popups without changing focus based on where the mouse is aimed
// (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) // Instead, focus will be restored to the window under the bottom-most closed popup.
// (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
if (g.IO.MouseClicked[1]) if (g.IO.MouseClicked[1])
{ {
// Find the top-most window between HoveredWindow and the front most Modal Window. // Find the top-most window between HoveredWindow and the front most Modal Window.
@ -3346,7 +3350,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
if (window == g.HoveredWindow) if (window == g.HoveredWindow)
hovered_window_above_modal = true; hovered_window_above_modal = true;
} }
ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
} }
} }
@ -3787,13 +3791,13 @@ void ImGui::NewFrame()
// Closing the focused window restore focus to the first active root window in descending z-order // Closing the focused window restore focus to the first active root window in descending z-order
if (g.NavWindow && !g.NavWindow->WasActive) if (g.NavWindow && !g.NavWindow->WasActive)
FocusPreviousWindowIgnoringOne(NULL); FocusTopMostWindowUnderOne(NULL, NULL);
// No window should be open at the beginning of the frame. // No window should be open at the beginning of the frame.
// But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
g.CurrentWindowStack.resize(0); g.CurrentWindowStack.resize(0);
g.BeginPopupStack.resize(0); g.BeginPopupStack.resize(0);
ClosePopupsOverWindow(g.NavWindow); ClosePopupsOverWindow(g.NavWindow, false);
// Docking // Docking
DockContextUpdateDocking(&g); DockContextUpdateDocking(&g);
@ -5349,7 +5353,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
if (flags & ImGuiWindowFlags_Popup) if (flags & ImGuiWindowFlags_Popup)
{ {
ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
window_just_activated_by_user |= (window != popup_ref.Window); window_just_activated_by_user |= (window != popup_ref.Window);
} }
@ -5399,7 +5403,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
CheckStacksSize(window, true); CheckStacksSize(window, true);
if (flags & ImGuiWindowFlags_Popup) if (flags & ImGuiWindowFlags_Popup)
{ {
ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
popup_ref.Window = window; popup_ref.Window = window;
g.BeginPopupStack.push_back(popup_ref); g.BeginPopupStack.push_back(popup_ref);
window->PopupId = popup_ref.PopupId; window->PopupId = popup_ref.PopupId;
@ -5912,10 +5916,32 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Update various regions. Variables they depends on are set above in this function. // Update various regions. Variables they depends on are set above in this function.
// FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
// NB: WindowBorderSize is included in WindowPadding _and_ ScrollbarSizes so we need to cancel one out.
window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x + ImMin(window->ScrollbarSizes.x, window->WindowBorderSize)));
window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y + ImMin(window->ScrollbarSizes.y, window->WindowBorderSize)));
// Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
window->OuterRectClipped = window->Rect();
if (window->DockIsActive)
window->OuterRectClipped.Min.y += window->TitleBarHeight();
window->OuterRectClipped.ClipWith(window->ClipRect);
// Inner rectangle
// We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
// Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - ImMax(window->ScrollbarSizes.x, window->WindowBorderSize);
window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - ImMax(window->ScrollbarSizes.y, window->WindowBorderSize);
// Inner clipping rectangle
// Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - window->WindowBorderSize)));
window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - window->WindowBorderSize)));
window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
// Setup drawing context // Setup drawing context
// (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
@ -6032,12 +6058,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Clear hit test shape every frame // Clear hit test shape every frame
window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
// Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
window->OuterRectClipped = window->Rect();
if (window->DockIsActive)
window->OuterRectClipped.Min.y += window->TitleBarHeight();
window->OuterRectClipped.ClipWith(window->ClipRect);
// Pressing CTRL+C while holding on a window copy its content to the clipboard // Pressing CTRL+C while holding on a window copy its content to the clipboard
// This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
// Maybe we can support CTRL+C on every element? // Maybe we can support CTRL+C on every element?
@ -6047,22 +6067,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
LogToClipboard(); LogToClipboard();
*/ */
// Inner rectangle
// We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
// Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
//window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
// Inner clipping rectangle
// Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{ {
// Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
@ -6272,6 +6276,9 @@ void ImGui::FocusWindow(ImGuiWindow* window)
//IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
} }
// Close popups if any
ClosePopupsOverWindow(window, false);
// Passing NULL allow to disable keyboard focus // Passing NULL allow to disable keyboard focus
if (!window) if (!window)
return; return;
@ -6296,10 +6303,18 @@ void ImGui::FocusWindow(ImGuiWindow* window)
BringWindowToDisplayFront(window); BringWindowToDisplayFront(window);
} }
void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window) void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
int start_idx = g.WindowsFocusOrder.Size - 1;
if (under_this_window != NULL)
{
int under_this_window_idx = FindWindowFocusIndex(under_this_window);
if (under_this_window_idx != -1)
start_idx = under_this_window_idx - 1;
}
for (int i = start_idx; i >= 0; i--)
{ {
// We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
ImGuiWindow* window = g.WindowsFocusOrder[i]; ImGuiWindow* window = g.WindowsFocusOrder[i];
@ -6315,6 +6330,7 @@ void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
return; return;
} }
} }
FocusWindow(NULL);
} }
void ImGui::SetNextItemWidth(float item_width) void ImGui::SetNextItemWidth(float item_width)
@ -7585,10 +7601,10 @@ void ImGui::OpenPopupEx(ImGuiID id)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* parent_window = g.CurrentWindow; ImGuiWindow* parent_window = g.CurrentWindow;
int current_stack_size = g.BeginPopupStack.Size; int current_stack_size = g.BeginPopupStack.Size;
ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. ImGuiPopupData 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.PopupId = id;
popup_ref.Window = NULL; popup_ref.Window = NULL;
popup_ref.ParentWindow = parent_window; popup_ref.SourceWindow = g.NavWindow;
popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenFrameCount = g.FrameCount;
popup_ref.OpenParentId = parent_window->IDStack.back(); popup_ref.OpenParentId = parent_window->IDStack.back();
popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
@ -7635,7 +7651,7 @@ bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
return false; return false;
} }
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.OpenPopupStack.empty()) if (g.OpenPopupStack.empty())
@ -7649,49 +7665,51 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
// Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
{ {
ImGuiPopupRef& popup = g.OpenPopupStack[popup_count_to_keep]; ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
if (!popup.Window) if (!popup.Window)
continue; continue;
IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
continue; continue;
// Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
bool popup_or_descendent_has_focus = false; bool popup_or_descendent_is_ref_window = false;
for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_has_focus; m++) for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
if (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow) if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
popup_or_descendent_has_focus = true; if (popup_window->RootWindow == ref_window->RootWindow)
if (!popup_or_descendent_has_focus) popup_or_descendent_is_ref_window = true;
if (!popup_or_descendent_is_ref_window)
break; break;
} }
} }
if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
{ {
//IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
ClosePopupToLevel(popup_count_to_keep, false); ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
} }
} }
void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under) void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
{ {
IM_ASSERT(remaining >= 0);
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
g.OpenPopupStack.resize(remaining); g.OpenPopupStack.resize(remaining);
// FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely. if (restore_focus_to_window_under_popup)
// Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing.
// The current code will set focus to the parent of the popup window which is incorrect.
// It rarely manifested until now because UpdateMouseMovingWindowNewFrame() would call FocusWindow() again on the clicked window,
// leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again.
// However if the clicked window has the _NoMove flag set we would be left with B focused.
// For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway,
// but we should inspect and fix this properly.
if (apply_focus_to_window_under)
{ {
if (g.NavLayer == 0) if (focus_window && !focus_window->WasActive && popup_window)
focus_window = NavRestoreLastChildNavWindow(focus_window); {
FocusWindow(focus_window); // Fallback
FocusTopMostWindowUnderOne(popup_window, NULL);
}
else
{
if (g.NavLayer == 0 && focus_window)
focus_window = NavRestoreLastChildNavWindow(focus_window);
FocusWindow(focus_window);
}
} }
} }
@ -8760,14 +8778,14 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
{ {
// Fallback manual-scroll when window has no navigable item // Fallback manual-scroll when window has no navigable item
if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight()); SetWindowScrollY(window, window->Scroll.y - window->InnerMainRect.GetHeight());
else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight()); SetWindowScrollY(window, window->Scroll.y + window->InnerMainRect.GetHeight());
} }
else else
{ {
const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); const float page_offset_y = ImMax(0.0f, window->InnerMainRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
float nav_scoring_rect_offset_y = 0.0f; float nav_scoring_rect_offset_y = 0.0f;
if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
{ {
@ -8790,7 +8808,7 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
return 0.0f; return 0.0f;
} }
static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--) for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
@ -8815,7 +8833,7 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
return; return;
const int i_current = FindWindowFocusIndex(g.NavWindowingTarget); const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
if (!window_target) if (!window_target)
window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
@ -8928,11 +8946,11 @@ static void ImGui::NavUpdateWindowing()
if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop)) if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop))
{ {
ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL; ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
ClearActiveID();
g.NavDisableHighlight = false; g.NavDisableHighlight = false;
g.NavDisableMouseHover = true; g.NavDisableMouseHover = true;
apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
ClosePopupsOverWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window, false);
ClearActiveID();
FocusWindow(apply_focus_window); FocusWindow(apply_focus_window);
if (apply_focus_window->NavLastIds[0] == 0) if (apply_focus_window->NavLastIds[0] == 0)
NavInitWindow(apply_focus_window, false); NavInitWindow(apply_focus_window, false);

View File

@ -2267,6 +2267,13 @@ static void ShowDemoWindowPopups()
if (ImGui::BeginMenu("Sub-menu")) if (ImGui::BeginMenu("Sub-menu"))
{ {
ImGui::MenuItem("Click me"); ImGui::MenuItem("Click me");
if (ImGui::Button("Stacked Popup"))
ImGui::OpenPopup("another popup");
if (ImGui::BeginPopup("another popup"))
{
ImGui::Text("I am the last one here.");
ImGui::EndPopup();
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
ImGui::EndPopup(); ImGui::EndPopup();
@ -4154,42 +4161,48 @@ static void ShowExampleAppCustomRendering(bool* p_open)
if (ImGui::BeginTabItem("Primitives")) if (ImGui::BeginTabItem("Primitives"))
{ {
static float sz = 36.0f; static float sz = 36.0f;
static float thickness = 4.0f; static float thickness = 3.0f;
static ImVec4 col = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f");
ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f");
ImGui::ColorEdit4("Color", &col.x); ImGui::ColorEdit4("Color", &colf.x);
const ImVec2 p = ImGui::GetCursorScreenPos(); const ImVec2 p = ImGui::GetCursorScreenPos();
const ImU32 col32 = ImColor(col); const ImU32 col = ImColor(colf);
float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; float x = p.x + 4.0f, y = p.y + 4.0f;
float spacing = 10.0f;
ImDrawCornerFlags corners_none = 0;
ImDrawCornerFlags corners_all = ImDrawCornerFlags_All;
ImDrawCornerFlags corners_tl_br = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight;
for (int n = 0; n < 2; n++) for (int n = 0; n < 2; n++)
{ {
// First line uses a thickness of 1.0, second line uses the configurable thickness // First line uses a thickness of 1.0f, second line uses the configurable thickness
float th = (n == 0) ? 1.0f : thickness; float th = (n == 0) ? 1.0f : thickness;
draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6, th); x += sz + spacing; // Hexagon draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 6, th); x += sz + spacing; // Hexagon
draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 20, th); x += sz + spacing; // Circle draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 20, th); x += sz + spacing; // Circle
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 0.0f, ImDrawCornerFlags_All, th); x += sz + spacing; draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, corners_none, th); x += sz + spacing; // Square
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_All, th); x += sz + spacing; draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_all, th); x += sz + spacing; // Square with all rounded corners
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight, th); x += sz + spacing; draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners
draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32, th); x += sz + spacing; draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th); x += sz + spacing; // Triangle
draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col32, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th); x += sz*0.4f + spacing; // Thin triangle
draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, th); x += sz + spacing; // Diagonal line draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col32, th); draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line
draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col, th);
x = p.x + 4; x = p.x + 4;
y += sz + spacing; y += sz + spacing;
} }
draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6); x += sz + spacing; // Hexagon draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 6); x += sz + spacing; // Hexagon
draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 32); x += sz + spacing; // Circle draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 32); x += sz + spacing; // Circle
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32); x += sz + spacing; draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f); x += sz + spacing; draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight); x += sz + spacing; draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners
draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32); x += sz + spacing; draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col32); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col32); x += spacing + spacing; // Vertical line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness)
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col32); x += sz; // Pixel (faster than AddLine) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing*2.0f; // Vertical line (faster than AddLine, but only handle integer thickness)
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine)
draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255));
ImGui::Dummy(ImVec2((sz + spacing) * 9.5f, (sz + spacing) * 3)); ImGui::Dummy(ImVec2((sz + spacing) * 9.8f, (sz + spacing) * 3));
ImGui::EndTabItem(); ImGui::EndTabItem();
} }

View File

@ -668,8 +668,8 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds. // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds.
// Those macros expects l-values. // Those macros expects l-values.
#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } #define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } }
#define IM_NORMALIZE2F_OVER_EPSILON_CLAMP(VX,VY,EPS,INVLENMAX) { float d2 = VX*VX + VY*VY; if (d2 > EPS) { float inv_len = 1.0f / ImSqrt(d2); if (inv_len > INVLENMAX) inv_len = INVLENMAX; VX *= inv_len; VY *= inv_len; } } #define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; }
// TODO: Thickness anti-aliased lines cap are missing their AA fringe. // TODO: Thickness anti-aliased lines cap are missing their AA fringe.
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
@ -731,7 +731,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
// Average normals // Average normals
float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f) IM_FIXNORMAL2F(dm_x, dm_y)
dm_x *= AA_SIZE; dm_x *= AA_SIZE;
dm_y *= AA_SIZE; dm_y *= AA_SIZE;
@ -786,7 +786,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
// Average normals // Average normals
float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f); IM_FIXNORMAL2F(dm_x, dm_y);
float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE); float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE);
float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE); float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE);
float dm_in_x = dm_x * half_inner_thickness; float dm_in_x = dm_x * half_inner_thickness;
@ -906,7 +906,7 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
const ImVec2& n1 = temp_normals[i1]; const ImVec2& n1 = temp_normals[i1];
float dm_x = (n0.x + n1.x) * 0.5f; float dm_x = (n0.x + n1.x) * 0.5f;
float dm_y = (n0.y + n1.y) * 0.5f; float dm_y = (n0.y + n1.y) * 0.5f;
IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f); IM_FIXNORMAL2F(dm_x, dm_y);
dm_x *= AA_SIZE * 0.5f; dm_x *= AA_SIZE * 0.5f;
dm_y *= AA_SIZE * 0.5f; dm_y *= AA_SIZE * 0.5f;

View File

@ -65,6 +65,7 @@ struct ImGuiColorMod; // Stacked color modifier, backup of modifie
struct ImGuiColumnData; // Storage data for a single column struct ImGuiColumnData; // Storage data for a single column
struct ImGuiColumns; // Storage data for a columns set struct ImGuiColumns; // Storage data for a columns set
struct ImGuiContext; // Main imgui context struct ImGuiContext; // Main imgui context
struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum
struct ImGuiDockContext; // Docking system context struct ImGuiDockContext; // Docking system context
struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes) struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes)
struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session) struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session)
@ -74,7 +75,7 @@ struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() intern
struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only
struct ImGuiNavMoveResult; // Result of a directional navigation move query result struct ImGuiNavMoveResult; // Result of a directional navigation move query result
struct ImGuiNextWindowData; // Storage for SetNexWindow** functions struct ImGuiNextWindowData; // Storage for SetNexWindow** functions
struct ImGuiPopupRef; // Storage for current popup stack struct ImGuiPopupData; // Storage for current popup stack
struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file
struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it
struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabBar; // Storage for a tab bar
@ -553,6 +554,14 @@ struct IMGUI_API ImRect
bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
}; };
// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
struct ImGuiDataTypeInfo
{
size_t Size; // Size in byte
const char* PrintFmt; // Default printf format for the type
const char* ScanFmt; // Default scanf format for the type
};
// Stacked color modifier, backup of modified data so we can restore it // Stacked color modifier, backup of modified data so we can restore it
struct ImGuiColorMod struct ImGuiColorMod
{ {
@ -660,15 +669,17 @@ struct ImGuiSettingsHandler
}; };
// Storage for current popup stack // Storage for current popup stack
struct ImGuiPopupRef struct ImGuiPopupData
{ {
ImGuiID PopupId; // Set on OpenPopup() ImGuiID PopupId; // Set on OpenPopup()
ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
ImGuiWindow* ParentWindow; // Set on OpenPopup() ImGuiWindow* SourceWindow; // Set on OpenPopup() copy of NavWindow at the time of opening the popup
int OpenFrameCount; // Set on OpenPopup() int OpenFrameCount; // Set on OpenPopup()
ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate 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 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 ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup
ImGuiPopupData() { PopupId = 0; Window = SourceWindow = NULL; OpenFrameCount = -1; OpenParentId = 0; }
}; };
struct ImGuiColumnData struct ImGuiColumnData
@ -987,8 +998,8 @@ struct ImGuiContext
ImVector<ImGuiColorMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector<ImGuiColorMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont()
ImVector<ImGuiPopupRef> OpenPopupStack; // Which popups are open (persistent) ImVector<ImGuiPopupData>OpenPopupStack; // Which popups are open (persistent)
ImVector<ImGuiPopupRef> BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) ImVector<ImGuiPopupData>BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame)
ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions
bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions
ImGuiCond NextTreeNodeOpenCond; ImGuiCond NextTreeNodeOpenCond;
@ -1563,7 +1574,7 @@ namespace ImGui
IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id);
IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
IMGUI_API void FocusWindow(ImGuiWindow* window); IMGUI_API void FocusWindow(ImGuiWindow* window);
IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window); IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window);
IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window);
IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window);
IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window);
@ -1643,8 +1654,8 @@ namespace ImGui
// Popups, Modals, Tooltips // Popups, Modals, Tooltips
IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void OpenPopupEx(ImGuiID id);
IMGUI_API void ClosePopupToLevel(int remaining, bool apply_focus_to_window_under); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack! IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack!
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true);
@ -1783,6 +1794,12 @@ namespace ImGui
template<typename T, typename FLOAT_T> IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos); template<typename T, typename FLOAT_T> IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos);
template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
// Data type helpers
IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type);
IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format);
IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format);
// InputText // InputText
IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool TempInputTextScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); IMGUI_API bool TempInputTextScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format);

View File

@ -104,11 +104,6 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
// [SECTION] Forward Declarations // [SECTION] Forward Declarations
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Data Type helpers
static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format);
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format);
// For InputTextEx() // For InputTextEx()
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data);
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
@ -1234,7 +1229,7 @@ void ImGui::Separator()
return; return;
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
// Those flags should eventually be overridable by the user // Those flags should eventually be overrideable by the user
ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected
if (flags & ImGuiSeparatorFlags_Vertical) if (flags & ImGuiSeparatorFlags_Vertical)
@ -1253,7 +1248,7 @@ void ImGui::Separator()
x1 += window->DC.Indent.x; x1 += window->DC.Indent.x;
const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. ItemSize(ImVec2(0.0f, 1.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit
if (!ItemAdd(bb, 0)) if (!ItemAdd(bb, 0))
{ {
if (window->DC.CurrentColumns) if (window->DC.CurrentColumns)
@ -1283,7 +1278,7 @@ void ImGui::VerticalSeparator()
float y1 = window->DC.CursorPos.y; float y1 = window->DC.CursorPos.y;
float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y; float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y;
const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2)); const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
ItemSize(ImVec2(bb.GetWidth(), 0.0f)); ItemSize(ImVec2(1.0f, 0.0f));
if (!ItemAdd(bb, 0)) if (!ItemAdd(bb, 0))
return; return;
@ -1570,6 +1565,7 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
// [SECTION] Data Type and Data Formatting Helpers [Internal] // [SECTION] Data Type and Data Formatting Helpers [Internal]
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// - PatchFormatStringFloatToInt() // - PatchFormatStringFloatToInt()
// - DataTypeGetInfo()
// - DataTypeFormatString() // - DataTypeFormatString()
// - DataTypeApplyOp() // - DataTypeApplyOp()
// - DataTypeApplyOpFromText() // - DataTypeApplyOpFromText()
@ -1577,13 +1573,6 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
// - RoundScalarWithFormat<>() // - RoundScalarWithFormat<>()
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
struct ImGuiDataTypeInfo
{
size_t Size;
const char* PrintFmt; // Unused
const char* ScanFmt;
};
static const ImGuiDataTypeInfo GDataTypeInfo[] = static const ImGuiDataTypeInfo GDataTypeInfo[] =
{ {
{ sizeof(char), "%d", "%d" }, // ImGuiDataType_S8 { sizeof(char), "%d", "%d" }, // ImGuiDataType_S8
@ -1628,7 +1617,13 @@ static const char* PatchFormatStringFloatToInt(const char* fmt)
return fmt; return fmt;
} }
static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type)
{
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
return &GDataTypeInfo[data_type];
}
int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format)
{ {
// Signedness doesn't matter when pushing integer arguments // Signedness doesn't matter when pushing integer arguments
if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32)
@ -1651,7 +1646,7 @@ static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType da
return 0; return 0;
} }
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2)
{ {
IM_ASSERT(op == '+' || op == '-'); IM_ASSERT(op == '+' || op == '-');
switch (data_type) switch (data_type)
@ -1703,7 +1698,7 @@ static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void*
// User can input math operators (e.g. +100) to edit a numerical values. // User can input math operators (e.g. +100) to edit a numerical values.
// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format) bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format)
{ {
while (ImCharIsBlankA(*buf)) while (ImCharIsBlankA(*buf))
buf++; buf++;
@ -1727,11 +1722,12 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
// Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.
IM_ASSERT(data_type < ImGuiDataType_COUNT); IM_ASSERT(data_type < ImGuiDataType_COUNT);
int data_backup[2]; int data_backup[2];
IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup)); const ImGuiDataTypeInfo* type_info = ImGui::DataTypeGetInfo(data_type);
memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size); IM_ASSERT(type_info->Size <= sizeof(data_backup));
memcpy(data_backup, data_ptr, type_info->Size);
if (format == NULL) if (format == NULL)
format = GDataTypeInfo[data_type].ScanFmt; format = type_info->ScanFmt;
// FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point..
int arg1i = 0; int arg1i = 0;
@ -1800,7 +1796,7 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
IM_ASSERT(0); IM_ASSERT(0);
} }
return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0; return memcmp(data_backup, data_ptr, type_info->Size) != 0;
} }
static float GetMinimumStepAtDecimalPrecision(int decimal_precision) static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
@ -2021,11 +2017,9 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa
return false; return false;
// Default format string when passing NULL // Default format string when passing NULL
// Patch old "%.0f" format string to use "%d", read function comments for more details.
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL) if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.)
format = PatchFormatStringFloatToInt(format); format = PatchFormatStringFloatToInt(format);
// Tabbing or CTRL-clicking on Drag turns it into an input box // Tabbing or CTRL-clicking on Drag turns it into an input box
@ -2466,11 +2460,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co
return false; return false;
// Default format string when passing NULL // Default format string when passing NULL
// Patch old "%.0f" format string to use "%d", read function comments for more details.
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL) if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.)
format = PatchFormatStringFloatToInt(format); format = PatchFormatStringFloatToInt(format);
// Tabbing or CTRL-clicking on Slider turns it into an input box // Tabbing or CTRL-clicking on Slider turns it into an input box
@ -2622,11 +2614,9 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
return false; return false;
// Default format string when passing NULL // Default format string when passing NULL
// Patch old "%.0f" format string to use "%d", read function comments for more details.
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL) if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.)
format = PatchFormatStringFloatToInt(format); format = PatchFormatStringFloatToInt(format);
const bool hovered = ItemHoverable(frame_bb, id); const bool hovered = ItemHoverable(frame_bb, id);
@ -2808,9 +2798,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style; ImGuiStyle& style = g.Style;
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL) if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
char buf[64]; char buf[64];
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format); DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format);
@ -5865,9 +5854,10 @@ void ImGui::EndMainMenuBar()
EndMenuBar(); EndMenuBar();
// When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
// FIXME: With this strategy we won't be able to restore a NULL focus.
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
FocusPreviousWindowIgnoringOne(g.NavWindow); FocusTopMostWindowUnderOne(g.NavWindow, NULL);
End(); End();
} }
@ -5993,31 +5983,36 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
if (menuset_is_open) if (menuset_is_open)
g.NavWindow = backed_nav_window; g.NavWindow = backed_nav_window;
bool want_open = false, want_close = false; bool want_open = false;
bool want_close = false;
if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
{ {
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
bool moving_within_opened_triangle = false; bool moving_toward_other_child_menu = false;
if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
{
if (ImGuiWindow* next_window = g.OpenPopupStack[g.BeginPopupStack.Size].Window)
{
// FIXME-DPI: Values should be derived from a master "scale" factor.
ImRect next_window_rect = next_window->Rect();
ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
//window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
}
}
want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL;
want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar))
{
// FIXME-DPI: Values should be derived from a master "scale" factor.
ImRect next_window_rect = child_menu_window->Rect();
ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
//GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
}
if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu)
want_close = true;
if (!menu_is_open && hovered && pressed) // Click to open
want_open = true;
else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open
want_open = true;
if (g.NavActivateId == id) if (g.NavActivateId == id)
{ {