#9580

Keyboard navigation scoring logic has a bias to minimize horizontal distance

OpenamuTBKTopened 1mo ago

Version/Branch of Dear ImGui:

Version 1.93.0 WIP, Branch: docking

Back-ends:

imgui_impl_win32.cpp + imgui_impl_dx11.cpp

Compiler, OS:

Windows 10 + MSVC 2026

Full config/build information:

Dear ImGui 1.93.0 WIP (19298)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1951
define: _MSVC_LANG=201402
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
IM_ASSERT: runs expression: OK. expand size: OK
--------------------------------
io.BackendPlatformName: imgui_impl_win32
io.BackendRendererName: imgui_impl_dx11
io.ConfigFlags: 0x00000483
 NavEnableKeyboard
 NavEnableGamepad
 DockingEnable
 ViewportsEnable
io.ConfigDpiScaleFonts
io.ConfigDpiScaleViewports
io.ConfigViewportsNoDecoration
io.ConfigViewportsNoDefaultParent
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00003C1E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 HasParentViewport
 RendererHasVtxOffset
 RendererHasTextures
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,128
io.Fonts->FontLoaderName: stb_truetype
io.DisplaySize: 1264.00,761.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

Hello, I am working on a asset picker/popup for my unreal plugin and running into some weird issues when using keyboard navigations. The way the interaction is supposed to work is that

  1. User opens the popup and it auto focues on search/filter widget
  2. User should then be able to press down to navigate to the list of items for selection. However due to how the Nav scoring works it is picking the wrong widget for navigation.

https://github.com/user-attachments/assets/97b98d18-208b-4424-a450-a19656664ec3

Since the setup currently compares distances b/w rect centers it tends to penalize bigger sized items. It prioritizes the side buttons over the list view (minimal repro shows it better, without any Unreal theme/style)

Any chance we could try to compare the edge distances instead of center? I tried with a very HACKY setup and it seems to work better but I am sure there would be other edge cases

//inside ImGui::NavScoreItem

float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items

// added this block to override values along move dir
if (g.NavMoveDir == ImGuiDir_Down)
{
    if (cand.Min.y > curr.Max.y)
    {
        dby = cand.Min.y - curr.Max.y;
    }
}
else if (g.NavMoveDir == ImGuiDir_Up)
{
    if (cand.Max.y < curr.Min.y)
    {
        dby = -curr.Min.y + cand.Max.y;
    }
}
else if (g.NavMoveDir == ImGuiDir_Right)
{
    if (cand.Min.x > curr.Max.x)
    {
        dbx = cand.Min.x - curr.Max.x;
    }
}
else if (g.NavMoveDir == ImGuiDir_Left)
{
    if (cand.Max.x < curr.Min.x)
    {
        dbx = -curr.Min.x + cand.Max.x;
    }
}

I can try using shortcuts API as a workaround but I really don't want to xD Are there any other ways to solve this? Should I use child windows instead of groups?

Screenshots/Video:

https://github.com/user-attachments/assets/c013da37-83a9-4c02-b393-082e3d727fcd

Here ListBox is getting a higher score due to its height(size).

Minimal, Complete and Verifiable Example code:

if (ImGui::Begin("BugRepro"))
{
    ImGui::BeginGroup();
    {
        char buf[32] = { 0 };
        ImGui::InputText("##InputText", buf, sizeof(buf));

        if (ImGui::BeginListBox("##ListBox"))
        {
            for (int i = 0; i < 32; ++i)
            {
                ImGui::PushID(i);
                ImGui::Selectable("Selectable");
                ImGui::PopID();
            }
            ImGui::EndListBox();
        }
    }
    ImGui::EndGroup();

    ImGui::SameLine();
    ImGui::BeginGroup();
    {
        ImGui::Button("1");
        ImGui::Button("2");
        ImGui::Button("3");
        ImGui::Button("4");
    }
    ImGui::EndGroup();
}
ImGui::End();

Activity

1
ocornut1mo ago

Any chance we could try to compare the edge distances instead of center?

The problem is proving the effect of changes on many cases/setups. At minimum it would need adding more cases to test_suite before we can tackle scoring issues. I'm certain there are other open issues related to Nav scoring and the effect of this X bias.