diff --git a/docs/TODO.txt b/docs/TODO.txt index 71f995f6f..dff2d24d2 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -334,9 +334,10 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: patterns to make it possible for arrows key to update selection (see JustMovedTo in range_select branch) - nav: restore/find nearest NavId when current one disappear (e.g. pressed a button that disappear, or perhaps auto restoring when current button change name) - nav: SetItemDefaultFocus() level of priority, so widget like Selectable when inside a popup could claim a low-priority default focus on the first selected iem + - nav: NavFlattened: init requests don't work properly on flattened siblings. + - nav: NavFlattened: pageup/pagedown/home/end don't work properly on flattened siblings. - nav: NavFlattened: ESC on a flattened child should select something. - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child are currently not considered to enter into a NavFlattened child. - - nav: NavFlattened: init request doesn't select items that are part of a NavFlattened child - nav: NavFlattened: cannot access menu-bar of a flattened child window with Alt/menu key (not a very common use case..). - nav: simulate right-click or context activation? (SHIFT+F10) - nav/tabbing: refactor old tabbing system and turn into navigation, should pass through all widgets (in submission order?). diff --git a/imgui.cpp b/imgui.cpp index e50eca852..196aa7b38 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -917,8 +917,8 @@ 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 void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb); +static void NavProcessItem(ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); @@ -7495,6 +7495,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu 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; @@ -7516,7 +7517,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(window, id, nav_bb_arg ? *nav_bb_arg : bb); // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX @@ -8879,20 +8880,22 @@ 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, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb) { + ImGuiContext& g = *GImGui; + IM_ASSERT(g.LastItemData.ID == id); // Otherwise pulling from window->DC wouldn't be right result->Window = window; result->ID = id; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->RectRel = nav_bb_rel; + result->RectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.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(ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb) { ImGuiContext& g = *GImGui; 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) @@ -8902,7 +8905,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) { @@ -8926,14 +8929,14 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con new_best = false; #endif if (new_best) - NavApplyItemToResult(result, window, id, nav_bb_rel); + NavApplyItemToResult(result, window, id, nav_bb); // 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, nav_bb)) - NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb_rel); + NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb); } } @@ -8944,7 +8947,7 @@ 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) } } @@ -8954,6 +8957,21 @@ bool ImGui::NavMoveRequestButNoResultYet() return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; } +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.NavMoveClipDir = clip_dir; + g.NavMoveFlags = move_flags; + g.NavMoveForwardToNextFrame = false; + g.NavMoveKeyMods = g.IO.KeyMods; + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisible.Clear(); + g.NavMoveResultOther.Clear(); +} + void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; @@ -9304,7 +9322,6 @@ void ImGui::NavUpdateCreateMoveRequest() 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); - g.NavMoveForwardToNextFrame = false; } else { @@ -9340,20 +9357,12 @@ void ImGui::NavUpdateCreateMoveRequest() } #endif - // If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match - // FIXME: Would be nice to call a single function to initiate a new request + // Submit + g.NavMoveForwardToNextFrame = false; if (g.NavMoveDir != ImGuiDir_None) - { - IM_ASSERT(window != NULL); - g.NavMoveSubmitted = g.NavMoveScoringItems = true; - g.NavMoveKeyMods = io.KeyMods; - g.NavMoveDirForDebug = g.NavMoveDir; - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisible.Clear(); - g.NavMoveResultOther.Clear(); - } + NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags); - // Moving with no reference triggers a init request + // 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); @@ -9507,6 +9516,7 @@ static void ImGui::NavUpdateCancelRequest() // 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() { diff --git a/imgui_internal.h b/imgui_internal.h index 7300623c1..94522e2c8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -758,7 +758,7 @@ enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // window->DC.LastItemDisplayRect is valid + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. @@ -1123,8 +1123,8 @@ struct ImGuiLastItemData ImGuiID ID; ImGuiItemFlags InFlags; // See ImGuiItemFlags_ ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ - ImRect Rect; - ImRect DisplayRect; + ImRect Rect; // Full rectangle + ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set) ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } }; @@ -2458,6 +2458,7 @@ namespace ImGui // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); IMGUI_API bool NavMoveRequestButNoResultYet(); + IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult();