summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bgfx/3rdparty/dear-imgui/imgui.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/bgfx/3rdparty/dear-imgui/imgui.cpp')
-rw-r--r--3rdparty/bgfx/3rdparty/dear-imgui/imgui.cpp1161
1 files changed, 683 insertions, 478 deletions
diff --git a/3rdparty/bgfx/3rdparty/dear-imgui/imgui.cpp b/3rdparty/bgfx/3rdparty/dear-imgui/imgui.cpp
index ef011070a79..4d8b3673cde 100644
--- a/3rdparty/bgfx/3rdparty/dear-imgui/imgui.cpp
+++ b/3rdparty/bgfx/3rdparty/dear-imgui/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.84 WIP
+// dear imgui, v1.85 WIP
// (main code and documentation)
// Help:
@@ -11,11 +11,14 @@
// - FAQ http://dearimgui.org/faq
// - Homepage & latest https://github.com/ocornut/imgui
// - Releases & changelog https://github.com/ocornut/imgui/releases
-// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!)
+// - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!)
// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
// - Issues & support https://github.com/ocornut/imgui/issues
-// - Discussions https://github.com/ocornut/imgui/discussions
+
+// Getting Started?
+// - For first-time users having issues compiling/linking/running or issues loading fonts:
+// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// See LICENSE.txt for copyright and licensing details (standard MIT License).
@@ -376,6 +379,7 @@ CODE
When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
+ - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead.
- 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
- ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
- ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
@@ -908,14 +912,14 @@ namespace ImGui
static void NavUpdate();
static void NavUpdateWindowing();
static void NavUpdateWindowingOverlay();
-static void NavUpdateMoveResult();
-static void NavUpdateInitResult();
+static void NavUpdateCancelRequest();
+static void NavUpdateCreateMoveRequest();
static float NavUpdatePageUpPageDown();
static inline void NavUpdateAnyRequestFlag();
static void NavEndFrame();
-static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand);
-static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
-static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
+static bool NavScoreItem(ImGuiNavItemData* result);
+static void NavApplyItemToResult(ImGuiNavItemData* result);
+static void NavProcessItem();
static ImVec2 NavCalcPreferredRefPos();
static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
@@ -990,7 +994,8 @@ static void* GImAllocatorUserData = NULL;
ImGuiStyle::ImGuiStyle()
{
- Alpha = 1.0f; // Global alpha applies to everything in ImGui
+ Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
+ DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
WindowPadding = ImVec2(8,8); // Padding within a window
WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
@@ -1182,6 +1187,24 @@ void ImGuiIO::ClearInputCharacters()
InputQueueCharacters.resize(0);
}
+void ImGuiIO::ClearInputKeys()
+{
+ memset(KeysDown, 0, sizeof(KeysDown));
+ for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++)
+ KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f;
+ KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
+ KeyMods = KeyModsPrev = ImGuiKeyModFlags_None;
+ for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++)
+ NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f;
+}
+
+void ImGuiIO::AddFocusEvent(bool focused)
+{
+ // We intentionally overwrite this and process in NewFrame(), in order to give a chance
+ // to multi-viewports backends to queue AddFocusEvent(false),AddFocusEvent(true) in same frame.
+ AppFocusLost = !focused;
+}
+
//-----------------------------------------------------------------------------
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
//-----------------------------------------------------------------------------
@@ -2238,21 +2261,21 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
return;
}
- // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
+ // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect
ImRect unclipped_rect = window->ClipRect;
- if (g.NavMoveRequest)
+ if (g.NavMoveScoringItems)
unclipped_rect.Add(g.NavScoringRect);
if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
- unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
+ unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); // Could store and use NavJustMovedToRectRel
const ImVec2 pos = window->DC.CursorPos;
int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
// When performing a navigation request, ensure we have one item extra in the direction we are moving to
- if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
+ if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up)
start--;
- if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
+ if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down)
end++;
start = ImClamp(start, 0, items_count);
@@ -2519,6 +2542,7 @@ struct ImGuiStyleVarInfo
static const ImGuiStyleVarInfo GStyleVarInfo[] =
{
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
+ { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
@@ -3086,7 +3110,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
if (id)
{
g.ActiveIdIsAlive = id;
- g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
+ g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
}
// Clear declaration of inputs claimed by the widget
@@ -3263,20 +3287,21 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return true;
}
-bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
+bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (!bb.Overlaps(window->ClipRect))
if (id == 0 || (id != g.ActiveId && id != g.NavId))
- if (clip_even_when_logged || !g.LogEnabled)
+ if (!g.LogEnabled)
return true;
return false;
}
// Called by ItemAdd()
// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
-void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
+// [WIP] This will eventually be refactored and moved into NavProcessItem()
+void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(id != 0 && id == g.LastItemData.ID);
@@ -3310,7 +3335,7 @@ void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
}
if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop)
{
- g.NavJustTabbedId = id;
+ g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing;
return;
}
@@ -3663,13 +3688,15 @@ static void ImGui::UpdateMouseInputs()
// Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
if (IsMousePosValid(&g.IO.MousePos))
- g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
+ g.IO.MousePos = g.MouseLastValidPos = ImFloor(g.IO.MousePos);
// If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
else
g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
+
+ // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
g.NavDisableMouseHover = false;
@@ -3851,6 +3878,7 @@ void ImGui::UpdateTabFocus()
void ImGui::UpdateHoveredWindowAndCaptureFlags()
{
ImGuiContext& g = *GImGui;
+ ImGuiIO& io = g.IO;
g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
// Find the window hovered by mouse:
@@ -3862,52 +3890,65 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
// Modal windows prevents mouse from hovering behind them.
ImGuiWindow* modal_window = GetTopMostPopupModal();
- if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window))
+ if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window, true))
clear_hovered_windows = true;
// Disabled mouse?
- if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
+ if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
clear_hovered_windows = true;
- // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
- int mouse_earliest_button_down = -1;
+ // We track click ownership. When clicked outside of a window the click is owned by the application and
+ // won't report hovering nor request capture even while dragging over our windows afterward.
+ const bool has_open_popup = (g.OpenPopupStack.Size > 0);
+ const bool has_open_modal = (modal_window != NULL);
+ int mouse_earliest_down = -1;
bool mouse_any_down = false;
- for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
+ for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
{
- if (g.IO.MouseClicked[i])
- g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
- mouse_any_down |= g.IO.MouseDown[i];
- if (g.IO.MouseDown[i])
- if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
- mouse_earliest_button_down = i;
+ if (io.MouseClicked[i])
+ {
+ io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
+ io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
+ }
+ mouse_any_down |= io.MouseDown[i];
+ if (io.MouseDown[i])
+ if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
+ mouse_earliest_down = i;
}
- const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
+ const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
+ const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
- if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
+ if (!mouse_avail && !mouse_dragging_extern_payload)
clear_hovered_windows = true;
if (clear_hovered_windows)
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
- // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
+ // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
+ // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
if (g.WantCaptureMouseNextFrame != -1)
- g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
+ {
+ io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
+ }
else
- g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
+ {
+ io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
+ io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
+ }
- // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
+ // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
if (g.WantCaptureKeyboardNextFrame != -1)
- g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
+ io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
else
- g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
- if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
- g.IO.WantCaptureKeyboard = true;
+ io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
+ if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
+ io.WantCaptureKeyboard = true;
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
- g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
+ io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
}
ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
@@ -4033,6 +4074,18 @@ void ImGui::NewFrame()
g.DragDropWithinTarget = false;
g.DragDropHoldJustPressedId = 0;
+ // Close popups on focus lost (currently wip/opt-in)
+ //if (g.IO.AppFocusLost)
+ // ClosePopupsExceptModals();
+
+ // Clear buttons state when focus is lost
+ // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle)
+ if (g.IO.AppFocusLost)
+ {
+ g.IO.ClearInputKeys();
+ g.IO.AppFocusLost = false;
+ }
+
// Update keyboard input state
// Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
g.IO.KeyMods = GetMergedKeyModFlags();
@@ -4472,6 +4525,7 @@ void ImGui::EndFrame()
// Clear Input data for next frame
g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
g.IO.InputQueueCharacters.resize(0);
+ g.IO.KeyModsPrev = g.IO.KeyMods; // doing it here is better than in NewFrame() as we'll tolerate backend writing to KeyMods. If we want to firmly disallow it we should detect it.
memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
@@ -5080,7 +5134,7 @@ void ImGui::EndChild()
ItemAdd(bb, window->ChildId);
RenderNavHighlight(bb, window->ChildId);
- // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
+ // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow)
RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
}
@@ -5726,9 +5780,11 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
{
window->ParentWindow = parent_window;
- window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
+ window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
window->RootWindow = parent_window->RootWindow;
+ if (parent_window && (flags & ImGuiWindowFlags_Popup))
+ window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
@@ -5807,12 +5863,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Add to stack
// We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
+ g.CurrentWindow = window;
ImGuiWindowStackData window_stack_data;
window_stack_data.Window = window;
window_stack_data.ParentLastItemDataBackup = g.LastItemData;
+ window_stack_data.StackSizesOnBegin.SetToCurrentState();
g.CurrentWindowStack.push_back(window_stack_data);
- g.CurrentWindow = window;
- window->DC.StackSizesOnBegin.SetToCurrentState();
g.CurrentWindow = NULL;
if (flags & ImGuiWindowFlags_Popup)
@@ -6195,16 +6251,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
}
- // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
+ // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
// When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
- // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
- // We also disabled this when we have dimming overlay behind this specific one child.
- // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected.
+ // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
{
bool render_decorations_in_parent = false;
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
- if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
+ {
+ // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
+ // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
+ ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
+ bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
+ bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0;
+ if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping)
render_decorations_in_parent = true;
+ }
if (render_decorations_in_parent)
window->DrawList = parent_window->DrawList;
@@ -6422,10 +6483,10 @@ void ImGui::End()
// Pop from window stack
g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
- g.CurrentWindowStack.pop_back();
if (window->Flags & ImGuiWindowFlags_Popup)
g.BeginPopupStack.pop_back();
- window->DC.StackSizesOnBegin.CompareWithCurrentState();
+ g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState();
+ g.CurrentWindowStack.pop_back();
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
}
@@ -6493,7 +6554,7 @@ void ImGui::FocusWindow(ImGuiWindow* window)
g.NavFocusScopeId = 0;
g.NavIdIsAlive = false;
g.NavLayer = ImGuiNavLayer_Main;
- g.NavInitRequest = g.NavMoveRequest = false;
+ g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
//IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
}
@@ -6600,29 +6661,38 @@ void ImGui::PopItemFlag()
g.CurrentItemFlags = g.ItemFlagsStack.back();
}
-// PushDisabled()/PopDisabled()
-// - Those can be nested but this cannot be used to enable an already disabled section (a single PushDisabled(true) in the stack is enough to keep things disabled)
-// - Those are not yet exposed in imgui.h because we are unsure of how to alter the style in a way that works for everyone.
-// We may rework this. Hypothetically, a future styling system may set a flag which make widgets use different colors.
+// BeginDisabled()/EndDisabled()
+// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
+// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
-// - You may trivially implement your own variation of this if needed.
-// Here we test (CurrentItemFlags & ImGuiItemFlags_Disabled) to allow nested PushDisabled() calls.
-void ImGui::PushDisabled(bool disabled)
+// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
+// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
+void ImGui::BeginDisabled(bool disabled)
{
ImGuiContext& g = *GImGui;
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
if (!was_disabled && disabled)
- PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.6f);
- PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled);
+ {
+ g.DisabledAlphaBackup = g.Style.Alpha;
+ g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
+ }
+ if (was_disabled || disabled)
+ g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
+ g.ItemFlagsStack.push_back(g.CurrentItemFlags);
+ g.DisabledStackSize++;
}
-void ImGui::PopDisabled()
+void ImGui::EndDisabled()
{
ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.DisabledStackSize > 0);
+ g.DisabledStackSize--;
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
- PopItemFlag();
+ //PopItemFlag();
+ g.ItemFlagsStack.pop_back();
+ g.CurrentItemFlags = g.ItemFlagsStack.back();
if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
- PopStyleVar();
+ g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
}
// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
@@ -6660,14 +6730,25 @@ void ImGui::PopTextWrapPos()
window->DC.TextWrapPosStack.pop_back();
}
-bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
+static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy)
+{
+ window = window->RootWindow;
+ if (popup_hierarchy)
+ window = window->RootWindowPopupTree;
+ return window;
+}
+
+bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy)
{
- if (window->RootWindow == potential_parent)
+ ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy);
+ if (window_root == potential_parent)
return true;
while (window != NULL)
{
if (window == potential_parent)
return true;
+ if (window == window_root) // end of chain
+ return false;
window = window->ParentWindow;
}
return false;
@@ -6691,37 +6772,31 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
{
IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
ImGuiContext& g = *GImGui;
- if (g.HoveredWindow == NULL)
+ ImGuiWindow* ref_window = g.HoveredWindow;
+ ImGuiWindow* cur_window = g.CurrentWindow;
+ if (ref_window == NULL)
return false;
if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
{
- ImGuiWindow* window = g.CurrentWindow;
- switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
- {
- case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
- if (g.HoveredWindow->RootWindow != window->RootWindow)
- return false;
- break;
- case ImGuiHoveredFlags_RootWindow:
- if (g.HoveredWindow != window->RootWindow)
- return false;
- break;
- case ImGuiHoveredFlags_ChildWindows:
- if (!IsWindowChildOf(g.HoveredWindow, window))
- return false;
- break;
- default:
- if (g.HoveredWindow != window)
- return false;
- break;
- }
+ IM_ASSERT(cur_window); // Not inside a Begin()/End()
+ const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
+ if (flags & ImGuiHoveredFlags_RootWindow)
+ cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
+
+ bool result;
+ if (flags & ImGuiHoveredFlags_ChildWindows)
+ result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
+ else
+ result = (ref_window == cur_window);
+ if (!result)
+ return false;
}
- if (!IsWindowContentHoverable(g.HoveredWindow, flags))
+ if (!IsWindowContentHoverable(ref_window, flags))
return false;
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
- if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
+ if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
return false;
return true;
}
@@ -6729,22 +6804,23 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
{
ImGuiContext& g = *GImGui;
+ ImGuiWindow* ref_window = g.NavWindow;
+ ImGuiWindow* cur_window = g.CurrentWindow;
+ if (ref_window == NULL)
+ return false;
if (flags & ImGuiFocusedFlags_AnyWindow)
- return g.NavWindow != NULL;
+ return true;
+ IM_ASSERT(cur_window); // Not inside a Begin()/End()
- IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
- switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
- {
- case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
- return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
- case ImGuiFocusedFlags_RootWindow:
- return g.NavWindow == g.CurrentWindow->RootWindow;
- case ImGuiFocusedFlags_ChildWindows:
- return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
- default:
- return g.NavWindow == g.CurrentWindow;
- }
+ const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
+ if (flags & ImGuiHoveredFlags_RootWindow)
+ cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
+
+ if (flags & ImGuiHoveredFlags_ChildWindows)
+ return IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
+ else
+ return (ref_window == cur_window);
}
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
@@ -7014,6 +7090,7 @@ void ImGui::ActivateItem(ImGuiID id)
{
ImGuiContext& g = *GImGui;
g.NavNextActivateId = id;
+ g.NavNextActivateFlags = ImGuiActivateFlags_None;
}
void ImGui::PushFocusScope(ImGuiID id)
@@ -7267,53 +7344,13 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
{
// PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
ImGuiContext& g = *GImGui;
- while (g.CurrentWindowStack.Size > 0)
+ while (g.CurrentWindowStack.Size > 0) //-V1044
{
- while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
- {
- if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
- EndTable();
- }
+ ErrorCheckEndWindowRecover(log_callback, user_data);
ImGuiWindow* window = g.CurrentWindow;
- IM_ASSERT(window != NULL);
- while (g.CurrentTabBar != NULL) //-V1044
- {
- if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
- EndTabBar();
- }
- while (window->DC.TreeDepth > 0)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
- TreePop();
- }
- while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
- EndGroup();
- }
- while (window->IDStack.Size > 1)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
- PopID();
- }
- while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
- PopStyleColor();
- }
- while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
- PopStyleVar();
- }
- while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
- PopFocusScope();
- }
if (g.CurrentWindowStack.Size == 1)
{
- IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
+ IM_ASSERT(window->IsFallbackWindow);
break;
}
IM_ASSERT(window == g.CurrentWindow);
@@ -7330,6 +7367,66 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
}
}
+// Must be called before End()/EndChild()
+void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)
+{
+ ImGuiContext& g = *GImGui;
+ while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
+ EndTable();
+ }
+
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;
+ IM_ASSERT(window != NULL);
+ while (g.CurrentTabBar != NULL) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
+ EndTabBar();
+ }
+ while (window->DC.TreeDepth > 0)
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
+ TreePop();
+ }
+ while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
+ EndGroup();
+ }
+ while (window->IDStack.Size > 1)
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
+ PopID();
+ }
+ while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name);
+ EndDisabled();
+ }
+ while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
+ PopStyleColor();
+ }
+ while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name);
+ PopItemFlag();
+ }
+ while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
+ PopStyleVar();
+ }
+ while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
+ PopFocusScope();
+ }
+}
+
// Save current stack sizes for later compare
void ImGuiStackSizes::SetToCurrentState()
{
@@ -7341,7 +7438,9 @@ void ImGuiStackSizes::SetToCurrentState()
SizeOfFontStack = (short)g.FontStack.Size;
SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
SizeOfGroupStack = (short)g.GroupStack.Size;
+ SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
+ SizeOfDisabledStack = (short)g.DisabledStackSize;
}
// Compare to detect usage errors
@@ -7359,6 +7458,8 @@ void ImGuiStackSizes::CompareWithCurrentState()
// For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!");
IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
+ IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!");
+ IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!");
IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!");
IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!");
IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!");
@@ -7393,7 +7494,6 @@ void ImGuiStackSizes::CompareWithCurrentState()
// - GetContentRegionMaxAbs() [Internal]
// - GetContentRegionAvail(),
// - GetWindowContentRegionMin(), GetWindowContentRegionMax()
-// - GetWindowContentRegionWidth()
// - BeginGroup()
// - EndGroup()
// Also see in imgui_widgets: tab bars, columns.
@@ -7443,15 +7543,17 @@ void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
// Declare item bounding box for clipping and interaction.
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
-bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags)
+bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
// Set item data
+ // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
g.LastItemData.ID = id;
g.LastItemData.Rect = bb;
- g.LastItemData.InFlags = g.CurrentItemFlags;
+ g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
+ g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags;
g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
// Directional navigation processing
@@ -7470,7 +7572,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
if (g.NavId == id || g.NavAnyRequest)
if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
- NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
+ NavProcessItem();
// [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
#ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
@@ -7489,15 +7591,15 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
#endif
// Clipping test
- const bool is_clipped = IsClippedEx(bb, id, false);
+ const bool is_clipped = IsClippedEx(bb, id);
if (is_clipped)
return false;
//if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
- // Tab stop handling (previously was using internal ItemFocusable() api)
- // FIXME-NAV: We would now want to move this above the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343)
- if (flags & ImGuiItemAddFlags_Focusable)
- ItemFocusable(window, id);
+ // [WIP] Tab stop handling (previously was using internal FocusableItemRegister() api)
+ // FIXME-NAV: We would now want to move this before the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343)
+ if (extra_flags & ImGuiItemFlags_Inputable)
+ ItemInputable(window, id);
// We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
if (IsMouseHoveringRect(bb.Min, bb.Max))
@@ -7761,14 +7863,9 @@ ImVec2 ImGui::GetWindowContentRegionMax()
return window->ContentRegionRect.Max - window->Pos;
}
-float ImGui::GetWindowContentRegionWidth()
-{
- ImGuiWindow* window = GImGui->CurrentWindow;
- return window->ContentRegionRect.GetWidth();
-}
-
// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
+// FIXME-OPT: Could we safely early out on ->SkipItems?
void ImGui::BeginGroup()
{
ImGuiContext& g = *GImGui;
@@ -8284,6 +8381,21 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to
}
}
+void ImGui::ClosePopupsExceptModals()
+{
+ ImGuiContext& g = *GImGui;
+
+ int popup_count_to_keep;
+ for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
+ {
+ ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
+ if (!window || window->Flags & ImGuiWindowFlags_Modal)
+ 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
+ ClosePopupToLevel(popup_count_to_keep, true);
+}
+
void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
{
ImGuiContext& g = *GImGui;
@@ -8419,7 +8531,7 @@ void ImGui::EndPopup()
IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
IM_ASSERT(g.BeginPopupStack.Size > 0);
- // Make all menus and popups wrap around for now, may need to expose that policy.
+ // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
if (g.NavWindow == window)
NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
@@ -8639,6 +8751,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
//-----------------------------------------------------------------------------
// FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked.
+// In our terminology those should be interchangeable. Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
{
ImGuiContext& g = *GImGui;
@@ -8669,7 +8782,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
window->NavLastIds[nav_layer] = id;
if (g.LastItemData.ID == id)
- window->NavRectRel[nav_layer] = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
+ window->NavRectRel[nav_layer] = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos);
if (g.ActiveIdSource == ImGuiInputSource_Nav)
g.NavDisableMouseHover = true;
@@ -8700,7 +8813,7 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect
r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
}
- else
+ else // FIXME: PageUp/PageDown are leaving move_dir == None
{
r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
@@ -8708,15 +8821,17 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect
}
// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
-static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
+static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (g.NavLayer != window->DC.NavLayerCurrent)
return false;
- const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
- g.NavScoringCount++;
+ // FIXME: Those are not good variables names
+ ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle
+ const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
+ g.NavScoringDebugCount++;
// When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
if (window->ParentWindow == g.NavWindow)
@@ -8778,24 +8893,24 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
- draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
+ draw_list->AddText(cand.Max, ~0U, buf);
}
else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
{
- if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
if (quadrant == g.NavMoveDir)
{
ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
ImDrawList* draw_list = GetForegroundDrawList(window);
draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
- draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
+ draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
}
}
#endif
// Is it in the quadrant we're interesting in moving to?
bool new_best = false;
- if (quadrant == g.NavMoveDir)
+ const ImGuiDir move_dir = g.NavMoveDir;
+ if (quadrant == move_dir)
{
// Does it beat the current best candidate?
if (dist_box < result->DistBox)
@@ -8817,7 +8932,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
// Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
// (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
// this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
- if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
+ if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
new_best = true;
}
}
@@ -8830,7 +8945,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
// Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
- if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
+ if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
{
result->DistAxial = dist_axial;
new_best = true;
@@ -8839,23 +8954,25 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
return new_best;
}
-static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
+static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
result->Window = window;
- result->ID = id;
+ result->ID = g.LastItemData.ID;
result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
- result->RectRel = nav_bb_rel;
+ result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos);
}
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
-static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
+// This is called after LastItemData is set.
+static void ImGui::NavProcessItem()
{
ImGuiContext& g = *GImGui;
- //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
- // return;
-
+ ImGuiWindow* window = g.CurrentWindow;
+ const ImGuiID id = g.LastItemData.ID;
+ const ImRect nav_bb = g.LastItemData.NavRect;
const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
- const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
// Process Init Request
if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
@@ -8865,7 +8982,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
if (candidate_for_nav_default_focus || g.NavInitResultId == 0)
{
g.NavInitResultId = id;
- g.NavInitResultRectRel = nav_bb_rel;
+ g.NavInitResultRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
}
if (candidate_for_nav_default_focus)
{
@@ -8875,27 +8992,22 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
}
// Process Move Request (scoring for navigation)
- // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
- if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
+ // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
+ if (g.NavMoveScoringItems)
{
- ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
-#if IMGUI_DEBUG_NAV_SCORING
- // [DEBUG] Score all items in NavWindow at all times
- if (!g.NavMoveRequest)
- g.NavMoveDir = g.NavMoveDirLast;
- bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
-#else
- bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
-#endif
- if (new_best)
- NavApplyItemToResult(result, window, id, nav_bb_rel);
+ if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
+ {
+ ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
+ if (NavScoreItem(result))
+ NavApplyItemToResult(result);
- // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
- const float VISIBLE_RATIO = 0.70f;
- if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
- if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
- if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
- NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel);
+ // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
+ const float VISIBLE_RATIO = 0.70f;
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
+ if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
+ if (NavScoreItem(&g.NavMoveResultLocalVisible))
+ NavApplyItemToResult(&g.NavMoveResultLocalVisible);
+ }
}
// Update window-relative bounding box of navigated item
@@ -8905,43 +9017,62 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
g.NavLayer = window->DC.NavLayerCurrent;
g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
g.NavIdIsAlive = true;
- window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
+ window->NavRectRel[window->DC.NavLayerCurrent] = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); // Store item bounding box (relative to window position)
}
}
bool ImGui::NavMoveRequestButNoResultYet()
{
ImGuiContext& g = *GImGui;
- return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
+ return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
+}
+
+// FIXME: ScoringRect is not set
+void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.NavWindow != NULL);
+ g.NavMoveSubmitted = g.NavMoveScoringItems = true;
+ g.NavMoveDir = move_dir;
+ g.NavMoveDirForDebug = move_dir;
+ g.NavMoveClipDir = clip_dir;
+ g.NavMoveFlags = move_flags;
+ g.NavMoveForwardToNextFrame = false;
+ g.NavMoveKeyMods = g.IO.KeyMods;
+ g.NavMoveResultLocal.Clear();
+ g.NavMoveResultLocalVisible.Clear();
+ g.NavMoveResultOther.Clear();
+ NavUpdateAnyRequestFlag();
}
void ImGui::NavMoveRequestCancel()
{
ImGuiContext& g = *GImGui;
- g.NavMoveRequest = false;
+ g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
}
-void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
+// Forward will reuse the move request again on the next frame (generally with modifications done to it)
+void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
{
ImGuiContext& g = *GImGui;
- IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
+ IM_ASSERT(g.NavMoveForwardToNextFrame == false);
NavMoveRequestCancel();
+ g.NavMoveForwardToNextFrame = true;
g.NavMoveDir = move_dir;
g.NavMoveClipDir = clip_dir;
- g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
- g.NavMoveRequestFlags = move_flags;
- g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
+ g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
}
-void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
+// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
+// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
+void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
{
ImGuiContext& g = *GImGui;
-
- // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
- // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
- g.NavWrapRequestWindow = window;
- g.NavWrapRequestFlags = move_flags;
+ IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
+ // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test
+ if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
+ g.NavMoveFlags |= wrap_flags;
}
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
@@ -8973,20 +9104,20 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
if (window->NavLastIds[layer] != 0)
{
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
- g.NavDisableHighlight = false;
- g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
else
{
g.NavLayer = layer;
NavInitWindow(window, true);
}
+ g.NavDisableHighlight = false;
+ g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
static inline void ImGui::NavUpdateAnyRequestFlag()
{
ImGuiContext& g = *GImGui;
- g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
+ g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
if (g.NavAnyRequest)
IM_ASSERT(g.NavWindow != NULL);
}
@@ -9031,7 +9162,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
// Mouse (we need a fallback in case the mouse becomes invalid after being used)
if (IsMousePosValid(&g.IO.MousePos))
return g.IO.MousePos;
- return g.LastValidMousePos;
+ return g.MouseLastValidPos;
}
else
{
@@ -9087,16 +9218,12 @@ static void ImGui::NavUpdate()
ImGuiIO& io = g.IO;
io.WantSetMousePos = false;
- g.NavWrapRequestWindow = NULL;
- g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
-#if 0
- if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
-#endif
+ //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
// Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
// (do it before we map Keyboard input!)
- bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
- bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
+ const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
+ const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
{
if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f
@@ -9119,16 +9246,6 @@ static void ImGui::NavUpdate()
io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
if (io.KeyShift)
io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
-
- // AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl)
- // But also even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
- if (io.KeyAlt && !io.KeyCtrl)
- io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
-
- // We automatically cancel toggling nav layer when any text has been typed while holding Alt. (See #370)
- if (io.KeyAlt && !io.KeyCtrl && g.NavWindowingToggleLayer && io.InputQueueCharacters.Size > 0)
- g.NavWindowingToggleLayer = false;
-
#undef NAV_MAP_KEY
}
memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
@@ -9137,24 +9254,16 @@ static void ImGui::NavUpdate()
// Process navigation init request (select first/default focus)
if (g.NavInitResultId != 0)
- NavUpdateInitResult();
+ NavInitRequestApplyResult();
g.NavInitRequest = false;
g.NavInitRequestFromMove = false;
g.NavInitResultId = 0;
g.NavJustMovedToId = 0;
// Process navigation move request
- if (g.NavMoveRequest)
- NavUpdateMoveResult();
-
- // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
- if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
- {
- IM_ASSERT(g.NavMoveRequest);
- if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
- g.NavDisableHighlight = false;
- g.NavMoveRequestForward = ImGuiNavForward_None;
- }
+ if (g.NavMoveSubmitted)
+ NavMoveRequestApplyResult();
+ g.NavMoveSubmitted = g.NavMoveScoringItems = false;
// Apply application mouse position movement, after we had a chance to process move request result.
if (g.NavMousePosDirty && g.NavIdIsAlive)
@@ -9165,6 +9274,7 @@ static void ImGui::NavUpdate()
{
io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
io.WantSetMousePos = true;
+ //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
}
g.NavMousePosDirty = false;
}
@@ -9172,7 +9282,7 @@ static void ImGui::NavUpdate()
g.NavJustTabbedId = 0;
IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
- // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
+ // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
if (g.NavWindow)
NavSaveLastChildNavWindowIntoParent(g.NavWindow);
if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
@@ -9186,116 +9296,51 @@ static void ImGui::NavUpdate()
io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
// Process NavCancel input (to close a popup, get back to parent, clear focus)
- if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
- {
- IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
- if (g.ActiveId != 0)
- {
- if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
- ClearActiveID();
- }
- else if (g.NavLayer != ImGuiNavLayer_Main)
- {
- // Leave the "menu" layer
- NavRestoreLayer(ImGuiNavLayer_Main);
- }
- else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
- {
- // Exit child window
- ImGuiWindow* child_window = g.NavWindow;
- ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
- IM_ASSERT(child_window->ChildId != 0);
- ImRect child_rect = child_window->Rect();
- FocusWindow(parent_window);
- SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
- }
- else if (g.OpenPopupStack.Size > 0)
- {
- // Close open popup/menu
- if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
- ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
- }
- else
- {
- // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
- if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
- g.NavWindow->NavLastIds[0] = 0;
- g.NavId = g.NavFocusScopeId = 0;
- }
- }
+ NavUpdateCancelRequest();
// Process manual activation request
- g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
+ g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0;
+ g.NavActivateFlags = ImGuiActivateFlags_None;
if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
{
bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
+ bool input_down = IsNavInputDown(ImGuiNavInput_Input);
bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
+ bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed);
if (g.ActiveId == 0 && activate_pressed)
+ {
g.NavActivateId = g.NavId;
+ g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
+ }
+ if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
+ {
+ g.NavActivateInputId = g.NavId;
+ g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
+ }
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
g.NavActivateDownId = g.NavId;
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
g.NavActivatePressedId = g.NavId;
- if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
- g.NavInputId = g.NavId;
}
if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
g.NavDisableHighlight = true;
if (g.NavActivateId != 0)
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
- g.NavMoveRequest = false;
// Process programmatic activation request
+ // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
if (g.NavNextActivateId != 0)
- g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
- g.NavNextActivateId = 0;
-
- // Initiate directional inputs request
- if (g.NavMoveRequestForward == ImGuiNavForward_None)
- {
- g.NavMoveDir = ImGuiDir_None;
- g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
- if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
- {
- const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
- if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
- }
- g.NavMoveClipDir = g.NavMoveDir;
- }
- else
{
- // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
- // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
- IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
- IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
- g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
+ if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput)
+ g.NavActivateInputId = g.NavNextActivateId;
+ else
+ g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
+ g.NavActivateFlags = g.NavNextActivateFlags;
}
+ g.NavNextActivateId = 0;
- // Update PageUp/PageDown/Home/End scroll
- // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
- float nav_scoring_rect_offset_y = 0.0f;
- if (nav_keyboard_active)
- nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
-
- // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
- if (g.NavMoveDir != ImGuiDir_None)
- {
- g.NavMoveRequest = true;
- g.NavMoveRequestKeyMods = io.KeyMods;
- g.NavMoveDirLast = g.NavMoveDir;
- }
- if (g.NavMoveRequest && g.NavId == 0)
- {
- IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
- g.NavInitRequest = g.NavInitRequestFromMove = true;
- // Reassigning with same value, we're being explicit here.
- g.NavInitResultId = 0; // -V1048
- g.NavDisableHighlight = false;
- }
+ // Process move requests
+ NavUpdateCreateMoveRequest();
NavUpdateAnyRequestFlag();
// Scrolling
@@ -9304,12 +9349,13 @@ static void ImGui::NavUpdate()
// *Fallback* manual-scroll with Nav directional keys when window has no navigable item
ImGuiWindow* window = g.NavWindow;
const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
- if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
+ const ImGuiDir move_dir = g.NavMoveDir;
+ if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None)
{
- if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
- SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
- if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
- SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
+ if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
+ SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
+ if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
+ SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
}
// *Normal* Manual scroll with NavScrollXXX keys
@@ -9321,37 +9367,15 @@ static void ImGui::NavUpdate()
SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
}
- // Reset search results
- g.NavMoveResultLocal.Clear();
- g.NavMoveResultLocalVisibleSet.Clear();
- g.NavMoveResultOther.Clear();
-
- // When using gamepad, we project the reference nav bounding box into window visible area.
- // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
- // (can't focus a visible object like we can with the mouse).
- if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main)
+ // Always prioritize mouse highlight if navigation is disabled
+ if (!nav_keyboard_active && !nav_gamepad_active)
{
- ImGuiWindow* window = g.NavWindow;
- ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
- if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
- {
- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
- float pad = window->CalcFontSize() * 0.5f;
- window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item
- window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
- g.NavId = g.NavFocusScopeId = 0;
- }
+ g.NavDisableHighlight = true;
+ g.NavDisableMouseHover = g.NavMousePosDirty = false;
}
- // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
- ImRect nav_rect_rel = g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted() ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
- g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0);
- g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
- g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
- g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
- IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
- //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
- g.NavScoringCount = 0;
+ // [DEBUG]
+ g.NavScoringDebugCount = 0;
#if IMGUI_DEBUG_NAV_RECTS
if (g.NavWindow)
{
@@ -9362,7 +9386,7 @@ static void ImGui::NavUpdate()
#endif
}
-static void ImGui::NavUpdateInitResult()
+void ImGui::NavInitRequestApplyResult()
{
// In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
ImGuiContext& g = *GImGui;
@@ -9373,6 +9397,7 @@ static void ImGui::NavUpdateInitResult()
// FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
+ g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
if (g.NavInitRequestFromMove)
{
g.NavDisableHighlight = false;
@@ -9380,13 +9405,114 @@ static void ImGui::NavUpdateInitResult()
}
}
-// Apply result from previous frame navigation directional move request
-static void ImGui::NavUpdateMoveResult()
+void ImGui::NavUpdateCreateMoveRequest()
{
ImGuiContext& g = *GImGui;
- if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
+ ImGuiIO& io = g.IO;
+ ImGuiWindow* window = g.NavWindow;
+
+ if (g.NavMoveForwardToNextFrame && window != NULL)
+ {
+ // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
+ // (preserve most state, which were already set by the NavMoveRequestForward() function)
+ IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
+ IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
+ IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
+ }
+ else
+ {
+ // Initiate directional inputs request
+ g.NavMoveDir = ImGuiDir_None;
+ g.NavMoveFlags = ImGuiNavMoveFlags_None;
+ if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
+ {
+ const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
+ }
+ g.NavMoveClipDir = g.NavMoveDir;
+ }
+
+ // Update PageUp/PageDown/Home/End scroll
+ // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
+ const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
+ float scoring_rect_offset_y = 0.0f;
+ if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
+ scoring_rect_offset_y = NavUpdatePageUpPageDown();
+
+ // [DEBUG] Always send a request
+#if IMGUI_DEBUG_NAV_SCORING
+ if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
+ g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
+ if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None)
+ {
+ g.NavMoveDir = g.NavMoveDirForDebug;
+ g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
+ }
+#endif
+
+ // Submit
+ g.NavMoveForwardToNextFrame = false;
+ if (g.NavMoveDir != ImGuiDir_None)
+ NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags);
+
+ // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match)
+ if (g.NavMoveSubmitted && g.NavId == 0)
+ {
+ IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
+ g.NavInitRequest = g.NavInitRequestFromMove = true;
+ g.NavInitResultId = 0;
+ g.NavDisableHighlight = false;
+ }
+
+ // When using gamepad, we project the reference nav bounding box into window visible area.
+ // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
+ // (can't focus a visible object like we can with the mouse).
+ if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)
+ {
+ ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
+ if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
+ {
+ IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
+ float pad = window->CalcFontSize() * 0.5f;
+ window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item
+ window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
+ g.NavId = g.NavFocusScopeId = 0;
+ }
+ }
+
+ // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
+ ImRect scoring_rect;
+ if (window != NULL)
+ {
+ ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
+ scoring_rect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max);
+ scoring_rect.TranslateY(scoring_rect_offset_y);
+ scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x);
+ scoring_rect.Max.x = scoring_rect.Min.x;
+ IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
+ //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
+ }
+ g.NavScoringRect = scoring_rect;
+}
+
+// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
+void ImGui::NavMoveRequestApplyResult()
+{
+ ImGuiContext& g = *GImGui;
+#if IMGUI_DEBUG_NAV_SCORING
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
+ return;
+#endif
+
+ // Select which result to use
+ ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
+
+ // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
+ if (result == NULL)
{
- // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
if (g.NavId != 0)
{
g.NavDisableHighlight = false;
@@ -9395,13 +9521,10 @@ static void ImGui::NavUpdateMoveResult()
return;
}
- // Select which result to use
- ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
-
// PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
- if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
- if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
- result = &g.NavMoveResultLocalVisibleSet;
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
+ if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
+ result = &g.NavMoveResultLocalVisible;
// Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
@@ -9413,7 +9536,7 @@ static void ImGui::NavUpdateMoveResult()
if (g.NavLayer == ImGuiNavLayer_Main)
{
ImVec2 delta_scroll;
- if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdge)
{
float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
delta_scroll.y = result->Window->Scroll.y - scroll_target;
@@ -9437,84 +9560,137 @@ static void ImGui::NavUpdateMoveResult()
// Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
g.NavJustMovedToId = result->ID;
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
- g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
+ g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
}
+
+ // Focus
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
+
+ // Enable nav highlight
g.NavDisableHighlight = false;
g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
+// Process NavCancel input (to close a popup, get back to parent, clear focus)
+// FIXME: In order to support e.g. Escape to clear a selection we'll need:
+// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
+// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
+static void ImGui::NavUpdateCancelRequest()
+{
+ ImGuiContext& g = *GImGui;
+ if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
+ return;
+
+ IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
+ if (g.ActiveId != 0)
+ {
+ if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
+ ClearActiveID();
+ }
+ else if (g.NavLayer != ImGuiNavLayer_Main)
+ {
+ // Leave the "menu" layer
+ NavRestoreLayer(ImGuiNavLayer_Main);
+ }
+ else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
+ {
+ // Exit child window
+ ImGuiWindow* child_window = g.NavWindow;
+ ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
+ IM_ASSERT(child_window->ChildId != 0);
+ ImRect child_rect = child_window->Rect();
+ FocusWindow(parent_window);
+ SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
+ }
+ else if (g.OpenPopupStack.Size > 0)
+ {
+ // Close open popup/menu
+ if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
+ ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
+ }
+ else
+ {
+ // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
+ if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
+ g.NavWindow->NavLastIds[0] = 0;
+ g.NavId = g.NavFocusScopeId = 0;
+ }
+}
+
// Handle PageUp/PageDown/Home/End keys
+// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
+// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
+// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
static float ImGui::NavUpdatePageUpPageDown()
{
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
- if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
- return 0.0f;
- if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
+ ImGuiWindow* window = g.NavWindow;
+ if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
return 0.0f;
- ImGuiWindow* window = g.NavWindow;
const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
- if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
+ if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
+ return 0.0f;
+
+ if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
{
- if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
+ // Fallback manual-scroll when window has no navigable item
+ if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
+ SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
+ else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
+ SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
+ else if (home_pressed)
+ SetScrollY(window, 0.0f);
+ else if (end_pressed)
+ SetScrollY(window, window->ScrollMax.y);
+ }
+ else
+ {
+ ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
+ const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
+ float nav_scoring_rect_offset_y = 0.0f;
+ if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
{
- // Fallback manual-scroll when window has no navigable item
- if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
- SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
- else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
- SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
- else if (home_pressed)
- SetScrollY(window, 0.0f);
- else if (end_pressed)
- SetScrollY(window, window->ScrollMax.y);
+ nav_scoring_rect_offset_y = -page_offset_y;
+ g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
+ g.NavMoveClipDir = ImGuiDir_Up;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
}
- else
+ else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
{
- ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
- const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
- float nav_scoring_rect_offset_y = 0.0f;
- if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
- {
- nav_scoring_rect_offset_y = -page_offset_y;
- g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
- g.NavMoveClipDir = ImGuiDir_Up;
- g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
- }
- else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
- {
- nav_scoring_rect_offset_y = +page_offset_y;
- g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
- g.NavMoveClipDir = ImGuiDir_Down;
- g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
- }
- else if (home_pressed)
- {
- // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
- // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
- // Preserve current horizontal position if we have any.
- nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
- if (nav_rect_rel.IsInverted())
- nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
- g.NavMoveDir = ImGuiDir_Down;
- g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
- }
- else if (end_pressed)
- {
- nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
- if (nav_rect_rel.IsInverted())
- nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
- g.NavMoveDir = ImGuiDir_Up;
- g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
- }
- return nav_scoring_rect_offset_y;
+ nav_scoring_rect_offset_y = +page_offset_y;
+ g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
+ g.NavMoveClipDir = ImGuiDir_Down;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
}
+ else if (home_pressed)
+ {
+ // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
+ // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
+ // Preserve current horizontal position if we have any.
+ nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
+ if (nav_rect_rel.IsInverted())
+ nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
+ g.NavMoveDir = ImGuiDir_Down;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
+ // FIXME-NAV: MoveClipDir left to _None, intentional?
+ }
+ else if (end_pressed)
+ {
+ nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
+ if (nav_rect_rel.IsInverted())
+ nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
+ g.NavMoveDir = ImGuiDir_Up;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
+ // FIXME-NAV: MoveClipDir left to _None, intentional?
+ }
+ return nav_scoring_rect_offset_y;
}
return 0.0f;
}
@@ -9528,13 +9704,14 @@ static void ImGui::NavEndFrame()
NavUpdateWindowingOverlay();
// Perform wrap-around in menus
- ImGuiWindow* window = g.NavWrapRequestWindow;
- ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
- if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
+ // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
+ ImGuiWindow* window = g.NavWindow;
+ const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
+ const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY;
+ if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
{
- IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
- ImRect bb_rel = window->NavRectRel[0];
-
+ bool do_forward = false;
+ ImRect bb_rel = window->NavRectRel[g.NavLayer];
ImGuiDir clip_dir = g.NavMoveDir;
if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
@@ -9545,7 +9722,7 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateY(-bb_rel.GetHeight());
clip_dir = ImGuiDir_Up;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
@@ -9555,7 +9732,7 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateY(+bb_rel.GetHeight());
clip_dir = ImGuiDir_Down;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
@@ -9566,7 +9743,7 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateX(-bb_rel.GetWidth());
clip_dir = ImGuiDir_Left;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
@@ -9576,7 +9753,12 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateX(+bb_rel.GetWidth());
clip_dir = ImGuiDir_Right;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
+ }
+ if (do_forward)
+ {
+ window->NavRectRel[g.NavLayer] = bb_rel;
+ NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags);
}
}
}
@@ -9621,6 +9803,8 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
static void ImGui::NavUpdateWindowing()
{
ImGuiContext& g = *GImGui;
+ ImGuiIO& io = g.IO;
+
ImGuiWindow* apply_focus_window = NULL;
bool apply_toggle_layer = false;
@@ -9632,25 +9816,26 @@ static void ImGui::NavUpdateWindowing()
// Fade out
if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
{
- g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
+ g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
g.NavWindowingTargetAnim = NULL;
}
// Start CTRL-TAB or Square+L/R window selection
- bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
- bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
+ const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
+ const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
+ const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && nav_keyboard_active && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab);
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
{
g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
- g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
+ g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
}
// Gamepad update
- g.NavWindowingTimer += g.IO.DeltaTime;
+ g.NavWindowingTimer += io.DeltaTime;
if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
{
// Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
@@ -9682,31 +9867,49 @@ static void ImGui::NavUpdateWindowing()
// Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
if (IsKeyPressedMap(ImGuiKey_Tab, true))
- NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
- if (!g.IO.KeyCtrl)
+ NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1);
+ if (!io.KeyCtrl)
apply_focus_window = g.NavWindowingTarget;
}
// Keyboard: Press and Release ALT to toggle menu layer
- // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB
- if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
+ // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.
+ // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
+ if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0)
+ {
g.NavWindowingToggleLayer = true;
- if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
- if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
- apply_toggle_layer = true;
+ g.NavInputSource = ImGuiInputSource_Keyboard;
+ }
+ if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
+ {
+ // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
+ // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
+ if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
+ g.NavWindowingToggleLayer = false;
+
+ // Apply layer toggle on release
+ // Important: we don't assume that Alt was previously held in order to handle loss of focus when backend calls io.AddFocusEvent(false)
+ // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
+ if (!(io.KeyMods & ImGuiKeyModFlags_Alt) && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) && g.NavWindowingToggleLayer)
+ if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
+ if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
+ apply_toggle_layer = true;
+ if (!io.KeyAlt)
+ g.NavWindowingToggleLayer = false;
+ }
// Move window
if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
{
ImVec2 move_delta;
- if (g.NavInputSource == ImGuiInputSource_Keyboard && !g.IO.KeyShift)
+ if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
if (g.NavInputSource == ImGuiInputSource_Gamepad)
move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
if (move_delta.x != 0.0f || move_delta.y != 0.0f)
{
const float NAV_MOVE_SPEED = 800.0f;
- const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
+ const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
MarkIniSettingsDirty(moving_window);
@@ -9757,14 +9960,16 @@ static void ImGui::NavUpdateWindowing()
FocusWindow(new_nav_window);
new_nav_window->NavLastChildNavWindow = old_nav_window;
}
- g.NavDisableHighlight = false;
- g.NavDisableMouseHover = true;
- // Reinitialize navigation when entering menu bar with the Alt key.
+ // Toggle layer
const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
- if (new_nav_layer == ImGuiNavLayer_Menu)
- g.NavWindow->NavLastIds[new_nav_layer] = 0;
- NavRestoreLayer(new_nav_layer);
+ if (new_nav_layer != g.NavLayer)
+ {
+ // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
+ if (new_nav_layer == ImGuiNavLayer_Menu)
+ g.NavWindow->NavLastIds[new_nav_layer] = 0;
+ NavRestoreLayer(new_nav_layer);
+ }
}
}
@@ -9857,14 +10062,16 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
return false;
if (g.ActiveIdMouseButton != -1)
mouse_button = g.ActiveIdMouseButton;
- if (g.IO.MouseDown[mouse_button] == false)
+ if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
return false;
g.ActiveIdAllowOverlap = false;
}
else
{
// Uncommon path: items without ID
- if (g.IO.MouseDown[mouse_button] == false)
+ if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
+ return false;
+ if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
return false;
// If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
@@ -9875,10 +10082,6 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
return false;
}
- // Early out
- if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
- return false;
-
// Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
// We build a throwaway ID based on current ID stack + relative AABB of items in window.
// THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
@@ -9935,7 +10138,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
{
ImGuiWindow* tooltip_window = g.CurrentWindow;
- tooltip_window->SkipItems = true;
+ tooltip_window->Hidden = tooltip_window->SkipItems = true;
tooltip_window->HiddenFramesCanSkipItems = 1;
}
}
@@ -10044,7 +10247,7 @@ bool ImGui::BeginDragDropTarget()
if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
return false;
ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
- if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
+ if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems)
return false;
const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
@@ -11273,7 +11476,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
- Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
+ Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId);
+ Text("NavActivateFlags: %04X", g.NavActivateFlags);
Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
@@ -11472,16 +11676,17 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list,
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
{
IM_ASSERT(show_mesh || show_aabb);
- ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
- ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
// Draw wire-frame version of all triangles
ImRect clip_rect = draw_cmd->ClipRect;
ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
ImDrawListFlags backup_flags = out_draw_list->Flags;
out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
- for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; )
+ for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
{
+ ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
+ ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
+
ImVec2 triangle[3];
for (int n = 0; n < 3; n++, idx_n++)
vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));