From 88a354585a87b69ad27b1570736a22860f78ca13 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 23:36:41 +0200 Subject: [PATCH] Nav: It's now possible to navigate sibling of a menu-bar while navigating inside one of their child. If a Left<>Right navigation request fails to find a match we forward the request to the root menu. (#787, #126) Currently the sibling menu is isn't automatically opened, that's still left to it (and even that can be anoying in Windows when the first menu-item is a child menu) --- imgui.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++------ imgui_internal.h | 11 ++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 08e697c40dd5..deeb7c3470ad 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2634,6 +2634,15 @@ static void NavUpdate() g.NavMoveFromClampedRefRect = false; } + // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame + if (g.NavMoveRequestForwardStep == 2) + { + IM_ASSERT(g.NavMoveRequest); + if (g.NavMoveResultId == 0) + g.NavDisableHighlight = false; + g.NavMoveRequestForwardStep = 0; + } + // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) { @@ -2781,14 +2790,24 @@ static void NavUpdate() // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; - g.NavMoveDir = ImGuiDir_None; - if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + if (g.NavMoveRequestForwardStep == 0) { - if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs)) + { + if ((allowed_dir_flags & (1<InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) @@ -5231,6 +5251,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; window->DC.ItemFlags = ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -9966,6 +9987,27 @@ void ImGui::EndMenuBar() ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; + ImGuiContext& g = *GImGui; + + // When a move request within one of our child menu failed, capture the request to navigate among our siblings. + if (g.NavMoveRequest && g.NavMoveResultId == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)) + { + ImGuiWindow* nav_earliest_child = g.NavWindow; + while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) + nav_earliest_child = nav_earliest_child->ParentWindow; + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForwardStep == 0) + { + // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check + FocusWindow(window); + SetNavIdAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); + g.NavLayer = 1; + g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. + g.NavMoveRequest = false; + g.NavMoveRequestForwardStep = 1; + } + } IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); IM_ASSERT(window->DC.MenuBarAppending); diff --git a/imgui_internal.h b/imgui_internal.h index 5b399b52797c..bf0876c308a4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -473,8 +473,9 @@ struct ImGuiContext ImGuiID NavInitDefaultResultId; ImRect NavInitDefaultResultRectRel; bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() - bool NavMoveRequest; // Move request for this frame bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items + bool NavMoveRequest; // Move request for this frame + int NavMoveRequestForwardStep; // 0: no forward, 1: forward request, 2: forward result ImGuiDir NavMoveDir; // West/East/North/South ImGuiDir NavMoveDirLast; // ImGuiID NavMoveResultId; // Best move request candidate @@ -585,6 +586,7 @@ struct ImGuiContext NavInitDefaultResultExplicit = false; NavMoveFromClampedRefRect = false; NavMoveRequest = false; + NavMoveRequestForwardStep = 0; NavMoveDir = NavMoveDirLast = ImGuiDir_None; NavMoveResultId = 0; NavMoveResultDistBox = NavMoveResultDistCenter = NavMoveResultDistAxial = 0.0f; @@ -663,8 +665,8 @@ struct IMGUI_API ImGuiDrawContext ImGuiID LastItemId; ImRect LastItemRect; bool LastItemRectHoveredRect; - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) - int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) + bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) + int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) int NavLayerActiveMask; // Which layer have been written to (result from previous frame) int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) bool MenuBarAppending; // FIXME: Remove this @@ -672,6 +674,7 @@ struct IMGUI_API ImGuiDrawContext ImVector ChildWindows; ImGuiStorage* StateStorage; ImGuiLayoutType LayoutType; + ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] @@ -714,7 +717,7 @@ struct IMGUI_API ImGuiDrawContext MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; - LayoutType = ImGuiLayoutType_Vertical; + LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; ItemWidth = 0.0f; ItemFlags = ImGuiItemFlags_Default_; TextWrapPos = -1.0f;