Shortcuts: reorganize route scoring so values are easier to read. (#9004)

Score now require 16-bits but ImGuiKeyRoutingData doesn't grow size.
This commit is contained in:
ocornut
2025-10-13 15:02:55 +02:00
parent 878c863af4
commit b6e277980f
2 changed files with 29 additions and 26 deletions

View File

@@ -9378,7 +9378,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner; routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
routing_entry->RoutingNextScore = 255; routing_entry->RoutingNextScore = 0;
if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner) if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
continue; continue;
rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
@@ -9447,23 +9447,24 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
return routing_data; return routing_data;
} }
// Current score encoding (lower is highest priority): // Current score encoding
// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive // - 0: Never route
// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active) // - 1: ImGuiInputFlags_RouteGlobal (lower priority)
// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused // - 100..199: ImGuiInputFlags_RouteFocused (if window in focus-stack)
// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack) // 200: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
// - 254: ImGuiInputFlags_RouteGlobal // 300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active)
// - 255: never route // 400: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
// - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority)
// 'flags' should include an explicit routing policy // 'flags' should include an explicit routing policy
static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (flags & ImGuiInputFlags_RouteFocused) if (flags & ImGuiInputFlags_RouteFocused)
{ {
// ActiveID gets top priority // ActiveID gets high priority
// (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
if (owner_id != 0 && g.ActiveId == owner_id) if (owner_id != 0 && g.ActiveId == owner_id)
return 1; return 300;
// Score based on distance to focused window (lower is better) // Score based on distance to focused window (lower is better)
// Assuming both windows are submitting a routing request, // Assuming both windows are submitting a routing request,
@@ -9473,25 +9474,27 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput
// - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
// This essentially follow the window->ParentWindowForFocusRoute chain. // This essentially follow the window->ParentWindowForFocusRoute chain.
if (focus_scope_id == 0) if (focus_scope_id == 0)
return 255; return 0;
for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
return 3 + index_in_focus_path; return 199 - index_in_focus_path;
return 255; return 0;
} }
else if (flags & ImGuiInputFlags_RouteActive) else if (flags & ImGuiInputFlags_RouteActive)
{ {
if (owner_id != 0 && g.ActiveId == owner_id) if (owner_id != 0 && g.ActiveId == owner_id)
return 1; return 300;
return 255; return 0;
} }
else if (flags & ImGuiInputFlags_RouteGlobal) else if (flags & ImGuiInputFlags_RouteGlobal)
{ {
if (flags & ImGuiInputFlags_RouteOverActive) if (flags & ImGuiInputFlags_RouteOverActive)
return 0; return 400;
if (owner_id != 0 && g.ActiveId == owner_id)
return 300;
if (flags & ImGuiInputFlags_RouteOverFocused) if (flags & ImGuiInputFlags_RouteOverFocused)
return 2; return 200;
return 254; return 1;
} }
IM_ASSERT(0); IM_ASSERT(0);
return 0; return 0;
@@ -9587,17 +9590,17 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, I
const int score = CalcRoutingScore(focus_scope_id, owner_id, flags); const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score); IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
if (score == 255) if (score == 0)
return false; return false;
// Submit routing for NEXT frame (assuming score is sufficient) // Submit routing for NEXT frame (assuming score is sufficient)
// FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <). // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >).
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
//const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore); //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore);
if (score < routing_data->RoutingNextScore) if (score > routing_data->RoutingNextScore)
{ {
routing_data->RoutingNext = owner_id; routing_data->RoutingNext = owner_id;
routing_data->RoutingNextScore = (ImU8)score; routing_data->RoutingNextScore = (ImU16)score;
} }
// Return routing state for CURRENT frame // Return routing state for CURRENT frame

View File

@@ -1556,12 +1556,12 @@ struct ImGuiKeyRoutingData
{ {
ImGuiKeyRoutingIndex NextEntryIndex; ImGuiKeyRoutingIndex NextEntryIndex;
ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits.
ImU8 RoutingCurrScore; // [DEBUG] For debug display ImU16 RoutingCurrScore; // [DEBUG] For debug display
ImU8 RoutingNextScore; // Lower is better (0: perfect score) ImU16 RoutingNextScore; // Lower is better (0: perfect score)
ImGuiID RoutingCurr; ImGuiID RoutingCurr;
ImGuiID RoutingNext; ImGuiID RoutingNext;
ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; }
}; };
// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.