mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-31 21:21:06 +01:00 
			
		
		
		
	Shortcut: ImGuiInputFlags_RouteFocused policy can filter Shortcuts conflicting with character input when an item is active. (#456)
This commit is contained in:
		
							
								
								
									
										48
									
								
								imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								imgui.cpp
									
									
									
									
									
								
							| @@ -3596,6 +3596,14 @@ void ImGui::Initialize() | |||||||
|     g.Viewports.push_back(viewport); |     g.Viewports.push_back(viewport); | ||||||
|     g.TempBuffer.resize(1024 * 3 + 1, 0); |     g.TempBuffer.resize(1024 * 3 + 1, 0); | ||||||
|  |  | ||||||
|  |     // Build KeysMayBeCharInput[] lookup table (1 bool per named key) | ||||||
|  |     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) | ||||||
|  |         if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9) | ||||||
|  |             || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period | ||||||
|  |             || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent | ||||||
|  |             || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual) | ||||||
|  |             g.KeysMayBeCharInput.SetBit(key); | ||||||
|  |  | ||||||
| #ifdef IMGUI_HAS_DOCK | #ifdef IMGUI_HAS_DOCK | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -8308,13 +8316,11 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) | |||||||
|     ImGuiContext& g = *GImGui; |     ImGuiContext& g = *GImGui; | ||||||
|     ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; |     ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; | ||||||
|     ImGuiKeyRoutingData* routing_data; |     ImGuiKeyRoutingData* routing_data; | ||||||
|     if (key_chord & ImGuiMod_Shortcut) |  | ||||||
|         key_chord = ConvertShortcutMod(key_chord); |  | ||||||
|     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); |     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); | ||||||
|     ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); |     ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); | ||||||
|     if (key == ImGuiKey_None) |     if (key == ImGuiKey_None) | ||||||
|         key = ConvertSingleModFlagToKey(&g, mods); |         key = ConvertSingleModFlagToKey(&g, mods); | ||||||
|     IM_ASSERT(IsNamedKey(key)); |     IM_ASSERT(IsNamedKey(key) && (key_chord & ImGuiMod_Shortcut) == 0); // Please call ConvertShortcutMod() in calling function. | ||||||
|  |  | ||||||
|     // Get (in the majority of case, the linked list will have one element so this should be 2 reads. |     // Get (in the majority of case, the linked list will have one element so this should be 2 reads. | ||||||
|     // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame). |     // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame). | ||||||
| @@ -8378,6 +8384,24 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // We need this to filter some Shortcut() routes when an item e.g. an InputText() is active | ||||||
|  | // e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be. | ||||||
|  | static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord) | ||||||
|  | { | ||||||
|  |     // Mimic 'ignore_char_inputs' logic in InputText() | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |  | ||||||
|  |     // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out. | ||||||
|  |     ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); | ||||||
|  |     const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Super)); | ||||||
|  |     if (ignore_char_inputs) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered. | ||||||
|  |     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); | ||||||
|  |     return g.KeysMayBeCharInput.TestBit(key); | ||||||
|  | } | ||||||
|  |  | ||||||
| // Request a desired route for an input chord (key + mods). | // Request a desired route for an input chord (key + mods). | ||||||
| // Return true if the route is available this frame. | // Return true if the route is available this frame. | ||||||
| // - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state. | // - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state. | ||||||
| @@ -8392,9 +8416,11 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI | |||||||
|         flags |= ImGuiInputFlags_RouteGlobalHigh; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut() |         flags |= ImGuiInputFlags_RouteGlobalHigh; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut() | ||||||
|     else |     else | ||||||
|         IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used |         IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used | ||||||
|  |     if (key_chord & ImGuiMod_Shortcut) | ||||||
|  |         key_chord = ConvertShortcutMod(key_chord); | ||||||
|  |  | ||||||
|     // [DEBUG] Debug break requested by user |     // [DEBUG] Debug break requested by user | ||||||
|     if (g.DebugBreakInShortcutRouting != 0 && g.DebugBreakInShortcutRouting == ConvertShortcutMod(key_chord)) |     if (g.DebugBreakInShortcutRouting == key_chord) | ||||||
|         IM_DEBUG_BREAK(); |         IM_DEBUG_BREAK(); | ||||||
|  |  | ||||||
|     if (flags & ImGuiInputFlags_RouteUnlessBgFocused) |     if (flags & ImGuiInputFlags_RouteUnlessBgFocused) | ||||||
| @@ -8408,6 +8434,18 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Specific culling for shortcuts with no modifiers when there's an active id. | ||||||
|  |     // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active. | ||||||
|  |     // but  Shortcut(Ctrl+G) should generally trigger when InputText() is active. | ||||||
|  |     // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active. | ||||||
|  |     // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined) | ||||||
|  |     if ((flags & ImGuiInputFlags_RouteFocused) && (g.ActiveId != 0 && g.ActiveId != owner_id)) | ||||||
|  |         if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord)) | ||||||
|  |         { | ||||||
|  |             IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> filtered as potential char input\n", GetKeyChordName(key_chord), owner_id, flags); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|     const int score = CalcRoutingScore(g.CurrentFocusScopeId, owner_id, flags); |     const int score = CalcRoutingScore(g.CurrentFocusScopeId, owner_id, flags); | ||||||
|     IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> score %d\n", GetKeyChordName(key_chord), owner_id, flags, score); |     IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> score %d\n", GetKeyChordName(key_chord), owner_id, flags, score); | ||||||
|     if (score == 255) |     if (score == 255) | ||||||
| @@ -8435,6 +8473,8 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI | |||||||
| bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id) | bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id) | ||||||
| { | { | ||||||
|     const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); |     const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); | ||||||
|  |     if (key_chord & ImGuiMod_Shortcut) | ||||||
|  |         key_chord = ConvertShortcutMod(key_chord); | ||||||
|     ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry. |     ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry. | ||||||
|     return routing_data->RoutingCurr == routing_id; |     return routing_data->RoutingCurr == routing_id; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								imgui.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								imgui.h
									
									
									
									
									
								
							| @@ -24,7 +24,7 @@ | |||||||
| // Library Version | // Library Version | ||||||
| // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') | // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') | ||||||
| #define IMGUI_VERSION       "1.90.2 WIP" | #define IMGUI_VERSION       "1.90.2 WIP" | ||||||
| #define IMGUI_VERSION_NUM   19012 | #define IMGUI_VERSION_NUM   19013 | ||||||
| #define IMGUI_HAS_TABLE | #define IMGUI_HAS_TABLE | ||||||
|  |  | ||||||
| /* | /* | ||||||
|   | |||||||
| @@ -1974,6 +1974,7 @@ struct ImGuiContext | |||||||
|     double                  LastKeyModsChangeTime;              // Record the last time key mods changed (affect repeat delay when using shortcut logic) |     double                  LastKeyModsChangeTime;              // Record the last time key mods changed (affect repeat delay when using shortcut logic) | ||||||
|     double                  LastKeyModsChangeFromNoneTime;      // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic) |     double                  LastKeyModsChangeFromNoneTime;      // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic) | ||||||
|     double                  LastKeyboardKeyPressTime;           // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed. |     double                  LastKeyboardKeyPressTime;           // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed. | ||||||
|  |     ImBitArrayForNamedKeys  KeysMayBeCharInput;                 // Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput(). sizeof() = 20 bytes | ||||||
|     ImGuiKeyOwnerData       KeysOwnerData[ImGuiKey_NamedKey_COUNT]; |     ImGuiKeyOwnerData       KeysOwnerData[ImGuiKey_NamedKey_COUNT]; | ||||||
|     ImGuiKeyRoutingTable    KeysRoutingTable; |     ImGuiKeyRoutingTable    KeysRoutingTable; | ||||||
|     ImU32                   ActiveIdUsingNavDirMask;            // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) |     ImU32                   ActiveIdUsingNavDirMask;            // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user