mirror of
https://github.com/Drezil/imgui.git
synced 2025-07-06 04:58:47 +02:00
Inputs: added wip/internal Input Owner system. (#456, #2637, #2620, #2891, #3370, #4828, #5108, #5242, #5641)
- Added SetKeyOwner(), SetItemKeyOwner(), TestKeyOwner(). - Added new IsKeyXXX IsMouseXXX functions with ImGuID owner_id and flags. - Obsoleted SetItemUsingMouseWheel(). (#2891) - Removed IsKeyPresseedEx() which was a recent internal addition 2022-07-08 deemed to be temporary exactly for this. - Added ImGuiButtonFlags_NoSetKeyOwner, ImGuiButtonFlags_NoTestKeyOwner - Added ImGuiSelectableFlags_NoSetKeyOwner. - Added ImGuiInputFlags_LockThisFrame, ImGuiInputFlags_LockUntilRelease for for SetKeyOwner(), SetItemKeyOwner(). - Added ImGuiInputFlags_CondXXX values for SetItemKeyOwner().
This commit is contained in:
280
imgui.cpp
280
imgui.cpp
@ -3546,7 +3546,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
|
||||
// Clear declaration of inputs claimed by the widget
|
||||
// (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
|
||||
g.ActiveIdUsingNavDirMask = 0x00;
|
||||
g.ActiveIdUsingKeyInputMask.ClearAllBits();
|
||||
g.ActiveIdUsingAllKeyboardKeys = false;
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
|
||||
g.ActiveIdUsingNavInputMask = 0x00;
|
||||
#endif
|
||||
@ -3562,7 +3562,6 @@ void ImGui::SetHoveredID(ImGuiID id)
|
||||
ImGuiContext& g = *GImGui;
|
||||
g.HoveredId = id;
|
||||
g.HoveredIdAllowOverlap = false;
|
||||
g.HoveredIdUsingMouseWheel = false;
|
||||
if (id != 0 && g.HoveredIdPreviousFrame != id)
|
||||
g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
|
||||
}
|
||||
@ -4218,12 +4217,23 @@ static void ImGui::UpdateKeyboardInputs()
|
||||
}
|
||||
|
||||
// Update keys
|
||||
for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++)
|
||||
for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++)
|
||||
{
|
||||
ImGuiKeyData* key_data = &io.KeysData[i];
|
||||
key_data->DownDurationPrev = key_data->DownDuration;
|
||||
key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
|
||||
}
|
||||
|
||||
// Update keys/input owner (named keys only): one entry per key
|
||||
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
|
||||
{
|
||||
ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET];
|
||||
ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
|
||||
owner_data->OwnerCurr = owner_data->OwnerNext;
|
||||
if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui::UpdateMouseInputs()
|
||||
@ -4312,13 +4322,9 @@ void ImGui::UpdateMouseWheel()
|
||||
LockWheelingWindow(NULL);
|
||||
}
|
||||
|
||||
const bool hovered_id_using_mouse_wheel = (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel);
|
||||
const bool active_id_using_mouse_wheel_x = g.ActiveIdUsingKeyInputMask.TestBit(ImGuiKey_MouseWheelX);
|
||||
const bool active_id_using_mouse_wheel_y = g.ActiveIdUsingKeyInputMask.TestBit(ImGuiKey_MouseWheelY);
|
||||
|
||||
ImVec2 wheel;
|
||||
wheel.x = (!hovered_id_using_mouse_wheel && !active_id_using_mouse_wheel_x) ? g.IO.MouseWheelH : 0.0f;
|
||||
wheel.y = (!hovered_id_using_mouse_wheel && !active_id_using_mouse_wheel_y) ? g.IO.MouseWheel : 0;
|
||||
wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_None) ? g.IO.MouseWheelH : 0.0f;
|
||||
wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_None) ? g.IO.MouseWheel : 0.0f;
|
||||
if (wheel.x == 0.0f && wheel.y == 0.0f)
|
||||
return;
|
||||
|
||||
@ -4546,10 +4552,8 @@ void ImGui::NewFrame()
|
||||
if (g.HoveredId && g.ActiveId != g.HoveredId)
|
||||
g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
|
||||
g.HoveredIdPreviousFrame = g.HoveredId;
|
||||
g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
|
||||
g.HoveredId = 0;
|
||||
g.HoveredIdAllowOverlap = false;
|
||||
g.HoveredIdUsingMouseWheel = false;
|
||||
g.HoveredIdDisabled = false;
|
||||
|
||||
// Clear ActiveID if the item is not alive anymore.
|
||||
@ -4577,7 +4581,10 @@ void ImGui::NewFrame()
|
||||
if (g.ActiveId == 0)
|
||||
{
|
||||
g.ActiveIdUsingNavDirMask = 0x00;
|
||||
g.ActiveIdUsingKeyInputMask.ClearAllBits();
|
||||
g.ActiveIdUsingAllKeyboardKeys = false;
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
|
||||
g.ActiveIdUsingNavInputMask = 0x00;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
|
||||
@ -4588,7 +4595,7 @@ void ImGui::NewFrame()
|
||||
// If your custom widget code used: { g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); }
|
||||
// Since IMGUI_VERSION_NUM >= 18804 it should be: { SetActiveIdUsingKey(ImGuiKey_Escape); SetActiveIdUsingKey(ImGuiKey_NavGamepadCancel); }
|
||||
if (g.ActiveIdUsingNavInputMask & (1 << ImGuiNavInput_Cancel))
|
||||
SetActiveIdUsingKey(ImGuiKey_Escape);
|
||||
SetKeyOwner(ImGuiKey_Escape, g.ActiveId);
|
||||
if (g.ActiveIdUsingNavInputMask & ~(1 << ImGuiNavInput_Cancel))
|
||||
IM_ASSERT(0); // Other values unsupported
|
||||
}
|
||||
@ -5388,29 +5395,13 @@ void ImGui::SetItemAllowOverlap()
|
||||
g.ActiveIdAllowOverlap = true;
|
||||
}
|
||||
|
||||
void ImGui::SetItemUsingMouseWheel()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiID id = g.LastItemData.ID;
|
||||
if (g.HoveredId == id)
|
||||
g.HoveredIdUsingMouseWheel = true;
|
||||
if (g.ActiveId == id)
|
||||
{
|
||||
g.ActiveIdUsingKeyInputMask.SetBit(ImGuiKey_MouseWheelX);
|
||||
g.ActiveIdUsingKeyInputMask.SetBit(ImGuiKey_MouseWheelY);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function.
|
||||
void ImGui::SetActiveIdUsingAllKeyboardKeys()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(g.ActiveId != 0);
|
||||
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
|
||||
g.ActiveIdUsingKeyInputMask.SetBitRange(ImGuiKey_Keyboard_BEGIN, ImGuiKey_Keyboard_END);
|
||||
//g.ActiveIdUsingKeyInputMask.SetBit(ImGuiKey_ModCtrl);
|
||||
//g.ActiveIdUsingKeyInputMask.SetBit(ImGuiKey_ModShift);
|
||||
//g.ActiveIdUsingKeyInputMask.SetBit(ImGuiKey_ModAlt);
|
||||
//g.ActiveIdUsingKeyInputMask.SetBit(ImGuiKey_ModSuper);
|
||||
g.ActiveIdUsingAllKeyboardKeys = true;
|
||||
NavMoveRequestCancel();
|
||||
}
|
||||
|
||||
@ -7950,7 +7941,7 @@ int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_ra
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiKeyData* key_data = GetKeyData(key);
|
||||
if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on input ownership)
|
||||
if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
|
||||
return 0;
|
||||
const float t = key_data->DownDuration;
|
||||
return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
|
||||
@ -7967,24 +7958,30 @@ ImVec2 ImGui::GetKeyVector2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key
|
||||
// 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)
|
||||
{
|
||||
return IsKeyDown(key, ImGuiKeyOwner_Any);
|
||||
}
|
||||
|
||||
bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
|
||||
{
|
||||
const ImGuiKeyData* key_data = GetKeyData(key);
|
||||
if (!key_data->Down)
|
||||
return false;
|
||||
if (!TestKeyOwner(key, owner_id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
|
||||
{
|
||||
return IsKeyPressedEx(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);
|
||||
return IsKeyPressed(key, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);
|
||||
}
|
||||
|
||||
// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
|
||||
// [Internal] 2022/07: Do not call this directly! It is a temporary entry point which we will soon replace with an overload for IsKeyPressed() when we introduce key ownership.
|
||||
bool ImGui::IsKeyPressedEx(ImGuiKey key, ImGuiInputFlags flags)
|
||||
// Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
|
||||
bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
|
||||
{
|
||||
const ImGuiKeyData* key_data = GetKeyData(key);
|
||||
if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on input ownership)
|
||||
if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
|
||||
return false;
|
||||
const float t = key_data->DownDuration;
|
||||
if (t < 0.0f)
|
||||
@ -7997,17 +7994,25 @@ bool ImGui::IsKeyPressedEx(ImGuiKey key, ImGuiInputFlags flags)
|
||||
GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
|
||||
pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
|
||||
}
|
||||
|
||||
if (!pressed)
|
||||
return false;
|
||||
if (!TestKeyOwner(key, owner_id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui::IsKeyReleased(ImGuiKey key)
|
||||
{
|
||||
return IsKeyReleased(key, ImGuiKeyOwner_Any);
|
||||
}
|
||||
|
||||
bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
|
||||
{
|
||||
const ImGuiKeyData* key_data = GetKeyData(key);
|
||||
if (key_data->DownDurationPrev < 0.0f || key_data->Down)
|
||||
return false;
|
||||
if (!TestKeyOwner(key, owner_id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8015,35 +8020,61 @@ bool ImGui::IsMouseDown(ImGuiMouseButton button)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
|
||||
return g.IO.MouseDown[button];
|
||||
return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.
|
||||
}
|
||||
|
||||
bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
|
||||
return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.
|
||||
}
|
||||
|
||||
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
|
||||
{
|
||||
return IsMouseClicked(button, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);
|
||||
}
|
||||
|
||||
bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
|
||||
if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on input ownership)
|
||||
if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
|
||||
return false;
|
||||
const float t = g.IO.MouseDownDuration[button];
|
||||
if (t == 0.0f)
|
||||
return true;
|
||||
if (repeat && t > g.IO.KeyRepeatDelay)
|
||||
return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
|
||||
return false;
|
||||
if (t < 0.0f)
|
||||
return false;
|
||||
|
||||
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);
|
||||
if (!pressed)
|
||||
return false;
|
||||
|
||||
if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui::IsMouseReleased(ImGuiMouseButton button)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
|
||||
return g.IO.MouseReleased[button];
|
||||
return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
|
||||
}
|
||||
|
||||
bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
|
||||
return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
|
||||
}
|
||||
|
||||
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
|
||||
return g.IO.MouseClickedCount[button] == 2;
|
||||
return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
|
||||
}
|
||||
|
||||
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
|
||||
@ -8305,6 +8336,98 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
|
||||
g.IO.ClearInputKeys();
|
||||
}
|
||||
|
||||
ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
|
||||
{
|
||||
if (!IsNamedKeyOrModKey(key))
|
||||
return ImGuiKeyOwner_None;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key);
|
||||
ImGuiID owner_id = owner_data->OwnerCurr;
|
||||
|
||||
if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId)
|
||||
if ((key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super)
|
||||
return ImGuiKeyOwner_None;
|
||||
|
||||
return owner_id;
|
||||
}
|
||||
|
||||
// TestKeyOwner(..., ID) : (owner == None || owner == ID)
|
||||
// TestKeyOwner(..., None) : (owner == None)
|
||||
// TestKeyOwner(..., Any) : no owner test
|
||||
// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
|
||||
bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
|
||||
{
|
||||
if (!IsNamedKeyOrModKey(key))
|
||||
return true;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId)
|
||||
if ((key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super)
|
||||
return false;
|
||||
|
||||
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key);
|
||||
if (owner_id == ImGuiKeyOwner_Any)
|
||||
return (owner_data->LockThisFrame == false);
|
||||
|
||||
// FIXME: For consistency with routing may be good to disable that, OR we could differentiate preemptive routing from setting owner on click.
|
||||
// For now it is better to disable so we can actually get report of case where this may matter.
|
||||
// -> Better to move in the SetKeyOwner() call, aka have a flag to set ->OwnerCurr in SetKeyOwner() ?
|
||||
// May simply be inconsistent and unneeded to offer that feature:
|
||||
// - for typical keyboard/gamepad routing with multiple claims and ServeLast, we don't want to affect owner testing during the frame.
|
||||
// - for typical mouse routing overlap + hovered window generally prevents making it useful to alter owner testing during the frame.
|
||||
// - but effectively Locked flag can alter same-frame behavior.
|
||||
//// We want OwnerNext to be handled immediately so SetKeyOwner(key, id1), TestKeyOwner(key, id2) == false
|
||||
//if (owner_data->OwnerNext != ImGuiKeyOwner_None && owner_data->OwnerNext != owner)
|
||||
// return false;
|
||||
|
||||
if (owner_data->OwnerCurr != owner_id)
|
||||
{
|
||||
if (owner_data->LockThisFrame)
|
||||
return false;
|
||||
if (owner_data->OwnerCurr != ImGuiKeyOwner_None)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
|
||||
// - SetKeyOwner(..., None) : clears owner
|
||||
// - SetKeyOwner(..., Any, !Lock) : illegal (assert)
|
||||
// - SetKeyOwner(..., Any or None, Lock) : set lock
|
||||
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)
|
||||
|
||||
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key);
|
||||
owner_data->OwnerNext = owner_id;
|
||||
|
||||
// We cannot lock by default as it would likely break lots of legacy code.
|
||||
// In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
|
||||
owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
|
||||
owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
|
||||
if (owner_data->LockThisFrame)
|
||||
owner_data->OwnerCurr = owner_id;
|
||||
}
|
||||
|
||||
// This is more or less equivalent to:
|
||||
// if (IsItemHovered() || IsItemActive())
|
||||
// SetKeyOwner(key, GetItemID());
|
||||
// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.
|
||||
// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
|
||||
// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.
|
||||
void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiID id = g.LastItemData.ID;
|
||||
if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
|
||||
return;
|
||||
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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ERROR CHECKING
|
||||
@ -10692,10 +10815,10 @@ void ImGui::NavUpdateCreateMoveRequest()
|
||||
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
|
||||
{
|
||||
const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateNavMove;
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadLeft, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_LeftArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; }
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadRight, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_RightArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; }
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadUp, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_UpArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; }
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadDown, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_DownArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Down; }
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; }
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; }
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; }
|
||||
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Down; }
|
||||
}
|
||||
g.NavMoveClipDir = g.NavMoveDir;
|
||||
g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
@ -10784,7 +10907,7 @@ void ImGui::NavUpdateCreateTabbingRequest()
|
||||
if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
|
||||
return;
|
||||
|
||||
const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
|
||||
const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
|
||||
if (!tab_pressed)
|
||||
return;
|
||||
|
||||
@ -10902,14 +11025,13 @@ static void ImGui::NavUpdateCancelRequest()
|
||||
ImGuiContext& g = *GImGui;
|
||||
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
|
||||
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
|
||||
if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, false)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, false)))
|
||||
if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, ImGuiKeyOwner_None)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, ImGuiKeyOwner_None)))
|
||||
return;
|
||||
|
||||
IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
|
||||
if (g.ActiveId != 0)
|
||||
{
|
||||
if (!IsActiveIdUsingKey(ImGuiKey_Escape) && !IsActiveIdUsingKey(ImGuiKey_NavGamepadCancel))
|
||||
ClearActiveID();
|
||||
ClearActiveID();
|
||||
}
|
||||
else if (g.NavLayer != ImGuiNavLayer_Main)
|
||||
{
|
||||
@ -10953,10 +11075,10 @@ static float ImGui::NavUpdatePageUpPageDown()
|
||||
if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
|
||||
return 0.0f;
|
||||
|
||||
const bool page_up_held = IsKeyDown(ImGuiKey_PageUp) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
|
||||
const bool page_down_held = IsKeyDown(ImGuiKey_PageDown) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
|
||||
const bool home_pressed = IsKeyPressed(ImGuiKey_Home) && !IsActiveIdUsingKey(ImGuiKey_Home);
|
||||
const bool end_pressed = IsKeyPressed(ImGuiKey_End) && !IsActiveIdUsingKey(ImGuiKey_End);
|
||||
const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_None);
|
||||
const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_None);
|
||||
const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat);
|
||||
const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat);
|
||||
if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
|
||||
return 0.0f;
|
||||
|
||||
@ -10966,9 +11088,9 @@ static float ImGui::NavUpdatePageUpPageDown()
|
||||
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
|
||||
{
|
||||
// Fallback manual-scroll when window has no navigable item
|
||||
if (IsKeyPressed(ImGuiKey_PageUp, true))
|
||||
if (IsKeyPressed(ImGuiKey_PageUp, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat))
|
||||
SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
|
||||
else if (IsKeyPressed(ImGuiKey_PageDown, true))
|
||||
else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat))
|
||||
SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
|
||||
else if (home_pressed)
|
||||
SetScrollY(window, 0.0f);
|
||||
@ -11156,8 +11278,8 @@ static void ImGui::NavUpdateWindowing()
|
||||
// Start CTRL+Tab or Square+L/R window selection
|
||||
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
|
||||
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
|
||||
const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, false);
|
||||
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab, false); // Note: enabled even without NavEnableKeyboard!
|
||||
const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, 0, ImGuiInputFlags_None);
|
||||
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab, ImGuiKeyOwner_None); // Note: enabled even without NavEnableKeyboard!
|
||||
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
|
||||
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
|
||||
{
|
||||
@ -11209,7 +11331,7 @@ static void ImGui::NavUpdateWindowing()
|
||||
// Keyboard: Press and Release ALT to toggle menu layer
|
||||
// - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.
|
||||
// - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
|
||||
if (nav_keyboard_active && IsKeyPressed(ImGuiMod_Alt))
|
||||
if (nav_keyboard_active && IsKeyPressed(ImGuiMod_Alt, ImGuiKeyOwner_None))
|
||||
{
|
||||
g.NavWindowingToggleLayer = true;
|
||||
g.NavInputSource = ImGuiInputSource_Keyboard;
|
||||
@ -11218,7 +11340,8 @@ static void ImGui::NavUpdateWindowing()
|
||||
{
|
||||
// We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
|
||||
// We cancel toggling nav layer when other modifiers are pressed. (See #4439)
|
||||
if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
|
||||
// We cancel toggling nav layer if an owner has claimed the key.
|
||||
if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_None) == false)
|
||||
g.NavWindowingToggleLayer = false;
|
||||
|
||||
// Apply layer toggle on release
|
||||
@ -12946,7 +13069,26 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
TreePop();
|
||||
}
|
||||
|
||||
// Misc Details
|
||||
if (TreeNode("Key Owners"))
|
||||
{
|
||||
TextUnformatted("Key Owners:");
|
||||
if (BeginListBox("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8)))
|
||||
{
|
||||
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
|
||||
{
|
||||
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key);
|
||||
if (owner_data->OwnerCurr == ImGuiKeyOwner_None)
|
||||
continue;
|
||||
Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
|
||||
owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
|
||||
DebugLocateItemOnHover(owner_data->OwnerCurr);
|
||||
}
|
||||
EndListBox();
|
||||
}
|
||||
Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
|
||||
TreePop();
|
||||
}
|
||||
|
||||
if (TreeNode("Internal state"))
|
||||
{
|
||||
Text("WINDOWING");
|
||||
@ -12962,11 +13104,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
|
||||
DebugLocateItemOnHover(g.ActiveId);
|
||||
Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
|
||||
|
||||
int active_id_using_key_input_count = 0;
|
||||
for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
|
||||
active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask[n] ? 1 : 0;
|
||||
Text("ActiveIdUsing: NavDirMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingNavDirMask, active_id_using_key_input_count);
|
||||
Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
|
||||
Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
|
||||
Text("HoverDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverDelayId, g.HoverDelayTimer, g.HoverDelayClearTimer);
|
||||
Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
|
||||
|
Reference in New Issue
Block a user