mirror of
https://github.com/ocornut/imgui.git
synced 2024-11-23 21:09:01 +08:00
Inputs: added wip/experiment routing system: Shortcut(), RoutingFocused policy, SetShortcutRouting(). (#456, #2637, #3724)
- InputText() uses Shortcut().
This commit is contained in:
parent
c59ebb2d71
commit
7ff2d3353c
201
imgui.cpp
201
imgui.cpp
@ -4116,6 +4116,45 @@ static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
|
||||
key_data->AnalogValue = analog_value;
|
||||
}
|
||||
|
||||
// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
|
||||
// Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D
|
||||
// Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6
|
||||
// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
|
||||
static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
rt->EntriesNext.resize(0);
|
||||
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
|
||||
{
|
||||
const int new_routing_start_idx = rt->EntriesNext.Size;
|
||||
ImGuiKeyRoutingData* routing_entry;
|
||||
for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
|
||||
{
|
||||
routing_entry = &rt->Entries[old_routing_idx];
|
||||
routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
|
||||
routing_entry->RoutingNext = ImGuiKeyOwner_None;
|
||||
routing_entry->RoutingNextScore = 255;
|
||||
if (routing_entry->RoutingCurr == ImGuiKeyOwner_None)
|
||||
continue;
|
||||
rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
|
||||
|
||||
// Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
|
||||
if (routing_entry->Mods == g.IO.KeyMods)
|
||||
{
|
||||
ImGuiKeyOwnerData* owner_data = ImGui::GetKeyOwnerData(key);
|
||||
if (owner_data->OwnerCurr == ImGuiKeyOwner_None)
|
||||
owner_data->OwnerCurr = routing_entry->RoutingCurr;
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite linked-list
|
||||
rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
|
||||
for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
|
||||
rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
|
||||
}
|
||||
rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
|
||||
}
|
||||
|
||||
// [Internal] Do not use directly (should read io.KeyMods instead)
|
||||
static ImGuiKeyChord GetMergedModsFromBools()
|
||||
{
|
||||
@ -4234,6 +4273,8 @@ static void ImGui::UpdateKeyboardInputs()
|
||||
owner_data->OwnerNext = ImGuiKeyOwner_None;
|
||||
owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore
|
||||
}
|
||||
|
||||
UpdateKeyRoutingTable(&g.KeysRoutingTable);
|
||||
}
|
||||
|
||||
static void ImGui::UpdateMouseInputs()
|
||||
@ -4794,6 +4835,9 @@ void ImGui::Shutdown()
|
||||
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
|
||||
g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
|
||||
g.MovingWindow = NULL;
|
||||
|
||||
g.KeysRoutingTable.Clear();
|
||||
|
||||
g.ColorStack.clear();
|
||||
g.StyleVarStack.clear();
|
||||
g.FontStack.clear();
|
||||
@ -7955,6 +7999,128 @@ ImVec2 ImGui::GetKeyVector2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key
|
||||
GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
|
||||
}
|
||||
|
||||
// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetID("") aka current ID stack.
|
||||
static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
return (owner_id != ImGuiKeyOwner_None && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentWindow->IDStack.back();
|
||||
}
|
||||
|
||||
ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
|
||||
{
|
||||
// Majority of shortcuts will be Key + any number of Mods
|
||||
// We accept _Single_ mod with ImGuiKey_None.
|
||||
// - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal
|
||||
// - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal
|
||||
// - Shortcut(ImGuiMod_Ctrl); // Legal
|
||||
// - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
|
||||
ImGuiKeyRoutingData* routing_data;
|
||||
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
|
||||
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
|
||||
if (key == ImGuiKey_None)
|
||||
key = ConvertSingleModFlagToKey(mods);
|
||||
IM_ASSERT(IsNamedKey(key));
|
||||
|
||||
// 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).
|
||||
for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
|
||||
{
|
||||
routing_data = &rt->Entries[idx];
|
||||
if (routing_data->Mods == mods)
|
||||
return routing_data;
|
||||
}
|
||||
|
||||
// Add
|
||||
ImGuiKeyRoutingIndex idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
|
||||
rt->Entries.push_back(ImGuiKeyRoutingData());
|
||||
routing_data = &rt->Entries[idx];
|
||||
routing_data->Mods = (ImU16)mods;
|
||||
routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
|
||||
rt->Index[key - ImGuiKey_NamedKey_BEGIN] = idx;
|
||||
return routing_data;
|
||||
}
|
||||
|
||||
// Request a desired route for an input chord (key + mods).
|
||||
// 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.
|
||||
// (Conceptually this does a "Submit for next frame" + "Test for current frame".
|
||||
// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
|
||||
// - Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
|
||||
// - Using 'owner_id == ImGuiKeyOwner_None': allows disabling/locking a shortcut.
|
||||
bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags, ImGuiWindow* location)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(owner_id != ImGuiKeyOwner_None);
|
||||
|
||||
// Calculate our score
|
||||
int score = 255;
|
||||
bool always_set_next_route = false;
|
||||
if (flags & ImGuiInputFlags_RouteFocused)
|
||||
{
|
||||
if (location == NULL)
|
||||
location = g.CurrentWindow;
|
||||
ImGuiWindow* focused = g.NavWindow;
|
||||
|
||||
if (g.ActiveId != 0 && g.ActiveId == owner_id)
|
||||
{
|
||||
// ActiveID gets top priority (0)
|
||||
// (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
|
||||
score = 0;
|
||||
}
|
||||
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)
|
||||
// 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++)
|
||||
{
|
||||
if (focused == location)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
routing_data->RoutingNext = routing_id;
|
||||
routing_data->RoutingNextScore = (ImU8)score;
|
||||
}
|
||||
|
||||
// Return routing state for CURRENT frame
|
||||
return routing_data->RoutingCurr == routing_id;
|
||||
}
|
||||
|
||||
// Currently unused by core (but used by tests)
|
||||
// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
|
||||
bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
|
||||
{
|
||||
const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
|
||||
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
|
||||
return routing_data->RoutingCurr == routing_id;
|
||||
}
|
||||
|
||||
// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
|
||||
// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
|
||||
bool ImGui::IsKeyDown(ImGuiKey key)
|
||||
@ -7986,6 +8152,7 @@ bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
|
||||
const float t = key_data->DownDuration;
|
||||
if (t < 0.0f)
|
||||
return false;
|
||||
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
|
||||
|
||||
bool pressed = (t == 0.0f);
|
||||
if (!pressed && ((flags & ImGuiInputFlags_Repeat) != 0))
|
||||
@ -8044,6 +8211,7 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInput
|
||||
const float t = g.IO.MouseDownDuration[button];
|
||||
if (t < 0.0f)
|
||||
return false;
|
||||
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
|
||||
|
||||
const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
|
||||
const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
|
||||
@ -8392,6 +8560,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
|
||||
void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
|
||||
{
|
||||
IM_ASSERT(IsNamedKeyOrModKey(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)
|
||||
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
|
||||
|
||||
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key);
|
||||
owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
|
||||
@ -8417,7 +8586,10 @@ void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
|
||||
if ((flags & ImGuiInputFlags_CondMask_) == 0)
|
||||
flags |= ImGuiInputFlags_CondDefault_;
|
||||
if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
|
||||
SetKeyOwner(key, id, flags);
|
||||
{
|
||||
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
|
||||
SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
|
||||
}
|
||||
}
|
||||
|
||||
// - Need to decide how to handle shortcut translations for Non-Mac <> Mac
|
||||
@ -8425,6 +8597,11 @@ void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
|
||||
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
|
||||
if (flags & ImGuiInputFlags_RouteFocused)
|
||||
if (!SetShortcutRouting(key_chord, owner_id, ImGuiInputFlags_RouteFocused, g.CurrentWindow))
|
||||
return false;
|
||||
|
||||
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
|
||||
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
|
||||
if (g.IO.KeyMods != mods)
|
||||
@ -8436,6 +8613,8 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags
|
||||
|
||||
if (!IsKeyPressed(key, owner_id, (flags & (ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_))))
|
||||
return false;
|
||||
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -13080,7 +13259,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
TreePop();
|
||||
}
|
||||
|
||||
if (TreeNode("Key Owners"))
|
||||
if (TreeNode("Key Owners & Shortcut Routing"))
|
||||
{
|
||||
TextUnformatted("Key Owners:");
|
||||
if (BeginListBox("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8)))
|
||||
@ -13096,6 +13275,24 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
}
|
||||
EndListBox();
|
||||
}
|
||||
TextUnformatted("Shortcut Routing:");
|
||||
if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8)))
|
||||
{
|
||||
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
|
||||
{
|
||||
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
|
||||
for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
|
||||
{
|
||||
char key_chord_name[64];
|
||||
ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
|
||||
GetKeyChordName(key | routing_data->Mods, key_chord_name, IM_ARRAYSIZE(key_chord_name));
|
||||
Text("%s: 0x%08X", key_chord_name, routing_data->RoutingCurr);
|
||||
DebugLocateItemOnHover(routing_data->RoutingCurr);
|
||||
idx = routing_data->NextEntryIndex;
|
||||
}
|
||||
}
|
||||
EndListBox();
|
||||
}
|
||||
Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
|
||||
TreePop();
|
||||
}
|
||||
|
2
imgui.h
2
imgui.h
@ -23,7 +23,7 @@
|
||||
// Library Version
|
||||
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345')
|
||||
#define IMGUI_VERSION "1.89 WIP"
|
||||
#define IMGUI_VERSION_NUM 18836
|
||||
#define IMGUI_VERSION_NUM 18837
|
||||
#define IMGUI_HAS_TABLE
|
||||
|
||||
/*
|
||||
|
@ -1277,6 +1277,32 @@ struct ImGuiInputEvent
|
||||
#define ImGuiKeyOwner_Any ((ImGuiID)0) // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitely used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
|
||||
#define ImGuiKeyOwner_None ((ImGuiID)-1) // Require key to have no owner.
|
||||
|
||||
typedef ImS16 ImGuiKeyRoutingIndex;
|
||||
|
||||
// Routing table entry (sizeof() == 16 bytes)
|
||||
struct ImGuiKeyRoutingData
|
||||
{
|
||||
ImGuiKeyRoutingIndex NextEntryIndex;
|
||||
ImU16 Mods;
|
||||
ImU8 RoutingNextScore; // Lower is better (0: perfect score)
|
||||
ImGuiID RoutingCurr;
|
||||
ImGuiID RoutingNext;
|
||||
|
||||
ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; }
|
||||
};
|
||||
|
||||
// Routing table maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.
|
||||
// Stored in main context (1 instance)
|
||||
struct ImGuiKeyRoutingTable
|
||||
{
|
||||
ImGuiKeyRoutingIndex Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[]
|
||||
ImVector<ImGuiKeyRoutingData> Entries;
|
||||
ImVector<ImGuiKeyRoutingData> EntriesNext; // Double-buffer to avoid reallocation (could use a shared buffer)
|
||||
|
||||
ImGuiKeyRoutingTable() { Clear(); }
|
||||
void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); }
|
||||
};
|
||||
|
||||
// This extend ImGuiKeyData but only for named keys (legacy keys don't support the new features)
|
||||
// Stored in main context (1 per named key). In the future might be merged into ImGuiKeyData.
|
||||
struct ImGuiKeyOwnerData
|
||||
@ -1310,6 +1336,16 @@ enum ImGuiInputFlags_
|
||||
// Flags for SetKeyOwner(), SetItemKeyOwner()
|
||||
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
|
||||
|
||||
// [Internal] Mask of which function support which flags
|
||||
ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_,
|
||||
ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteFocused,
|
||||
ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,
|
||||
ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1707,10 +1743,12 @@ struct ImGuiContext
|
||||
ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation.
|
||||
float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
|
||||
|
||||
// [EXPERIMENTAL] Key/Input Ownership
|
||||
// [EXPERIMENTAL] Key/Input Ownership + Shortcut Routing system
|
||||
// - The idea is that instead of "eating" a given key, we can link to an owner.
|
||||
// - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID.
|
||||
// - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame().
|
||||
ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT];
|
||||
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)
|
||||
bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
|
||||
@ -2817,12 +2855,19 @@ namespace ImGui
|
||||
IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
|
||||
IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id);
|
||||
|
||||
// [EXPERIMENTAL] Shortcuts
|
||||
// - ImGuiKeyChord = any ImGuiKey optionally ORed with ImGuiMod_XXX values.
|
||||
// [EXPERIMENTAL] Shortcut Routing
|
||||
// - ImGuiKeyChord = a ImGuiKey optionally OR-red with ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
|
||||
// ImGuiKey_C (accepted by functions taking ImGuiKey or ImGuiKeyChord)
|
||||
// ImGuiKey_C | ImGuiMod_Ctrl (accepted by functions taking ImGuiKeyChord)
|
||||
// - ONLY ImGuiMod_XXX values are legal to 'OR' with an ImGuiKey. You CANNOT 'OR' two ImGuiKey values.
|
||||
// ONLY ImGuiMod_XXX values are legal to 'OR' with an ImGuiKey. You CANNOT 'OR' two ImGuiKey values.
|
||||
// - When using one of the routing flags (e.g. ImGuiInputFlags_RouteFocused): routes requested ahead of time given a chord (key + modifiers) and a routing policy.
|
||||
// - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.
|
||||
// - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route.
|
||||
// - Multiple read sites may use a same owner and will all get the granted route.
|
||||
IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
|
||||
IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0, ImGuiWindow* location = NULL);
|
||||
IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id);
|
||||
IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord);
|
||||
|
||||
// [EXPERIMENTAL] Focus Scope
|
||||
// This is generally used to identify a unique input location (for e.g. a selection set)
|
||||
|
@ -4287,25 +4287,24 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
|
||||
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
|
||||
const bool is_osx = io.ConfigMacOSXBehaviors;
|
||||
const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiMod_Super | ImGuiMod_Shift));
|
||||
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
|
||||
const bool is_ctrl_key_only = (io.KeyMods == ImGuiMod_Ctrl);
|
||||
const bool is_shift_key_only = (io.KeyMods == ImGuiMod_Shift);
|
||||
const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiMod_Super) : (io.KeyMods == ImGuiMod_Ctrl);
|
||||
|
||||
const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
|
||||
const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C, false)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert, false))) && !is_password && (!is_multiline || state->HasSelection());
|
||||
const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly;
|
||||
const bool is_undo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable);
|
||||
const bool is_redo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable;
|
||||
const bool is_select_all = is_shortcut_key && IsKeyPressed(ImGuiKey_A, false);
|
||||
// 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)
|
||||
// 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());
|
||||
const bool is_copy = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_C, id, shortcut_flags & ~ImGuiInputFlags_Repeat) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, id, shortcut_flags & ~ImGuiInputFlags_Repeat)) && !is_password && (!is_multiline || state->HasSelection());
|
||||
const bool is_paste = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_V, id, shortcut_flags) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, id, shortcut_flags)) && !is_readonly;
|
||||
const bool is_undo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Z, id, shortcut_flags)) && !is_readonly && is_undoable;
|
||||
const bool is_redo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Y, id, shortcut_flags) || (is_osx && Shortcut(ImGuiMod_Shortcut | ImGuiMod_Shift | ImGuiKey_Z, id, shortcut_flags))) && !is_readonly && is_undoable;
|
||||
const bool is_select_all = Shortcut(ImGuiMod_Shortcut | ImGuiKey_A, id, shortcut_flags & ~ImGuiInputFlags_Repeat);
|
||||
|
||||
// We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
|
||||
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
|
||||
const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true);
|
||||
const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false));
|
||||
const bool is_cancel = IsKeyPressed(ImGuiKey_Escape, false) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, false));
|
||||
const bool is_cancel = Shortcut(ImGuiKey_Escape, id, shortcut_flags) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, false));
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
|
||||
else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
|
||||
|
Loading…
Reference in New Issue
Block a user