Переглянути джерело

Inputs: added routing priorities. (#456, #2637, #3724)

- and ImGuiInputFlags_RouteUnlessBgFocused
- will be useful for blind menu handlers.
ocornut 2 роки тому
батько
коміт
4d6a9ef93f
3 змінених файлів з 51 додано та 21 видалено
  1. 34 16
      imgui.cpp
  2. 16 4
      imgui_internal.h
  3. 1 1
      imgui_widgets.cpp

+ 34 - 16
imgui.cpp

@@ -8052,11 +8052,24 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
 bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags, ImGuiWindow* location)
 {
     ImGuiContext& g = *GImGui;
+    if ((flags & ImGuiInputFlags_RouteMask_) == 0)
+        flags = ImGuiInputFlags_RouteGlobalHigh; // This is the default for SetShortcutRouting() but NOT Shortcut() which doesn't touch routing by default!
+    else
+        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used
     IM_ASSERT(owner_id != ImGuiKeyOwner_None);
 
-    // Calculate our score
+    if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
+        if (g.NavWindow == NULL)
+            return false;
+
+    // Current score encoding (lower is highest priority):
+    //  -   0: ImGuiInputFlags_RouteGlobalHigh
+    //  -   1: ImGuiInputFlags_RouteFocused (if item active)
+    //  -   2: ImGuiInputFlags_RouteGlobal
+    //  -  3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
+    //  - 254: ImGuiInputFlags_RouteGlobalLow
+    //  - 255: none
     int score = 255;
-    bool always_set_next_route = false;
     if (flags & ImGuiInputFlags_RouteFocused)
     {
         if (location == NULL)
@@ -8065,44 +8078,48 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI
 
         if (g.ActiveId != 0 && g.ActiveId == owner_id)
         {
-            // ActiveID gets top priority (0)
+            // ActiveID gets top priority
             // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
-            score = 0;
+            score = 1;
         }
         else if (focused != NULL && focused->RootWindow == location->RootWindow) // Early out
         {
             // Score based on distance to focused window (lower is better)
             // Assuming both windows are submitting a routing request,
-            // - When WindowA...... is focused -> WindowA scores 1 (best), WindowA/ChildB scores 255 (no match)
-            // - When Window/ChildB is focused -> WindowA scores 2,        WindowA/ChildB scores 1 (best)
+            // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
+            // - When Window/ChildB is focused -> Window scores 4,        Window/ChildB scores 3 (best)
             // Assuming only WindowA is submitting a routing request,
-            // - When Window/ChildB is focused -> WindowA scores 2 (best), WindowA/ChildB doesn't have a scoe.
-            for (int next_score = 1; focused != NULL; next_score++)
+            // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
+            for (int next_score = 3; focused != NULL; next_score++)
             {
                 if (focused == location)
                 {
+                    IM_ASSERT(next_score < 255);
                     score = (ImU8)next_score;
                     break;
                 }
                 focused = (focused->RootWindow != focused) ? focused->ParentWindow : NULL; // FIXME: This could be later abstracted as a focus path
             }
         }
-
-        if (score == 255)
-            return false;
     }
     else
     {
-        score = 0;
-        always_set_next_route = true;
+        if (flags & ImGuiInputFlags_RouteGlobal)
+            score = 2;
+        else if (flags & ImGuiInputFlags_RouteGlobalLow)
+            score = 254;
+        else // ImGuiInputFlags_RouteGlobalHigh is default, so call to SetShorcutRouting() without no flags are not conditional
+            score = 0;
     }
+    if (score == 255)
+        return false;
 
     // 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 <).
     ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
     const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); // FIXME: Location
     //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
-    if (score < routing_data->RoutingNextScore || always_set_next_route)
+    if (score < routing_data->RoutingNextScore)
     {
         routing_data->RoutingNext = routing_id;
         routing_data->RoutingNextScore = (ImU8)score;
@@ -8598,8 +8615,9 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags
 {
     ImGuiContext& g = *GImGui;
 
-    if (flags & ImGuiInputFlags_RouteFocused)
-        if (!SetShortcutRouting(key_chord, owner_id, ImGuiInputFlags_RouteFocused, g.CurrentWindow))
+    // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter  with this, so IsKeyPressed() is fine with he 0/Any.
+    if (flags & (ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteUnlessBgFocused))
+        if (!SetShortcutRouting(key_chord, owner_id, flags, g.CurrentWindow))
             return false;
 
     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);

+ 16 - 4
imgui_internal.h

@@ -1337,13 +1337,25 @@ enum ImGuiInputFlags_
     ImGuiInputFlags_LockThisFrame       = 1 << 6,   // Access to key data will requires EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.
     ImGuiInputFlags_LockUntilRelease    = 1 << 7,   // Access to key data will requires EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when key is released or at end of frame is not down. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.
 
-    // Flags for Shortcut(), SetShortcutRouting()
-    // When Focus Routing is enabled, function will call SetShortcutRouting(): Accept inputs if currently in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.
-    ImGuiInputFlags_RouteFocused        = 1 << 8,   // Enable focus routing
+    // Routing policies for Shortcut(), SetShortcutRouting()
+    // - When a policy is set, Shortcut() will register itself with SetShortcutRouting(),
+    //   allowing the system to decide where to route the input among other route-aware calls.
+    //   The general idea is that several callers register a shortcut, and only one gets it.
+    // - Routing is NOT registered by default, meaning that a simple Shortcut() call
+    //   will see all inputs, won't have any side-effect and won't interfere with other inputs.
+    // - Priorities (highest-to-lowest): GlobalHigh > Focused (when active item) > Global > Focused (when focused window) > GlobalLow.
+    // - Can select only 1 policy among all available.
+    ImGuiInputFlags_RouteNone           = 0,        // Do not register route (provided for completeness but technically zero-value)
+    ImGuiInputFlags_RouteFocused        = 1 << 8,   // Register route if focused: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.
+    ImGuiInputFlags_RouteGlobalLow      = 1 << 9,   // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority.
+    ImGuiInputFlags_RouteGlobal         = 1 << 10,  // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText).
+    ImGuiInputFlags_RouteGlobalHigh     = 1 << 11,  // Register route globally (highest priority: unlikely you need to use that: will interfere with every active items)
+    ImGuiInputFlags_RouteMask_          = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh,
+    ImGuiInputFlags_RouteUnlessBgFocused= 1 << 12,  // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.
 
     // [Internal] Mask of which function support which flags
     ImGuiInputFlags_SupportedByIsKeyPressed     = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_,
-    ImGuiInputFlags_SupportedByShortcut         = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteFocused,
+    ImGuiInputFlags_SupportedByShortcut         = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteUnlessBgFocused,
     ImGuiInputFlags_SupportedBySetKeyOwner      = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,
     ImGuiInputFlags_SupportedBySetItemKeyOwner  = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,
 };

+ 1 - 1
imgui_widgets.cpp

@@ -4290,7 +4290,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl
         const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
 
-        // Using Shortcut() with ImGuiInputFlags_FocusRouting flag to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText)
+        // Using Shortcut() with ImGuiInputFlags_RouteFocused flag to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText)
         // Otherwise we could simply assume that we own the keys as we are active.
         const ImGuiInputFlags shortcut_flags = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_Repeat;
         const bool is_cut   = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_X, id, shortcut_flags) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, id, shortcut_flags)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());