|
@@ -1,4 +1,4 @@
|
|
|
-// dear imgui, v1.89.1
|
|
|
+// dear imgui, v1.89.2 WIP
|
|
|
// (main code and documentation)
|
|
|
|
|
|
// Help:
|
|
@@ -403,6 +403,11 @@ CODE
|
|
|
the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
|
|
|
the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
|
|
|
exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway.
|
|
|
+ - 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
|
|
|
+ this will require uses of legacy backend-dependent indices to be casted, e.g.
|
|
|
+ - with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
|
|
|
+ - with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A')
|
|
|
+ - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
|
|
|
- 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr);
|
|
|
- 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
|
|
|
- DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
|
|
@@ -1379,6 +1384,7 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
|
|
|
IM_ASSERT(&g.IO == this && "Can only add events to current context.");
|
|
|
IM_ASSERT(ImGui::IsNamedKeyOrModKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
|
|
|
IM_ASSERT(!ImGui::IsAliasKey(key)); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
|
|
|
+ IM_ASSERT(key != ImGuiMod_Shortcut); // We could easily support the translation here but it seems saner to not accept it (TestEngine perform a translation itself)
|
|
|
|
|
|
// Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
|
|
|
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
|
|
@@ -2928,11 +2934,14 @@ bool ImGuiListClipper::Step()
|
|
|
if (need_items_height && ItemsHeight > 0.0f)
|
|
|
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
|
|
|
if (ret)
|
|
|
+ {
|
|
|
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
|
|
|
+ }
|
|
|
else
|
|
|
+ {
|
|
|
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
|
|
|
- if (!ret)
|
|
|
End();
|
|
|
+ }
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -4321,16 +4330,15 @@ static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
|
|
|
rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
|
|
|
}
|
|
|
|
|
|
-// [Internal] Do not use directly (should read io.KeyMods instead)
|
|
|
-static ImGuiKeyChord GetMergedModsFromBools()
|
|
|
+// [Internal] Do not use directly
|
|
|
+static ImGuiKeyChord GetMergedModsFromKeys()
|
|
|
{
|
|
|
- ImGuiContext& g = *GImGui;
|
|
|
- ImGuiKeyChord key_chord = 0;
|
|
|
- if (g.IO.KeyCtrl) { key_chord |= ImGuiMod_Ctrl; }
|
|
|
- if (g.IO.KeyShift) { key_chord |= ImGuiMod_Shift; }
|
|
|
- if (g.IO.KeyAlt) { key_chord |= ImGuiMod_Alt; }
|
|
|
- if (g.IO.KeySuper) { key_chord |= ImGuiMod_Super; }
|
|
|
- return key_chord;
|
|
|
+ ImGuiKeyChord mods = 0;
|
|
|
+ if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; }
|
|
|
+ if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; }
|
|
|
+ if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; }
|
|
|
+ if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; }
|
|
|
+ return mods;
|
|
|
}
|
|
|
|
|
|
static void ImGui::UpdateKeyboardInputs()
|
|
@@ -4403,16 +4411,24 @@ static void ImGui::UpdateKeyboardInputs()
|
|
|
#undef NAV_MAP_KEY
|
|
|
}
|
|
|
#endif
|
|
|
-
|
|
|
#endif
|
|
|
|
|
|
- // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools, update aliases
|
|
|
- io.KeyMods = GetMergedModsFromBools();
|
|
|
+ // Update aliases
|
|
|
for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
|
|
|
UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
|
|
|
UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
|
|
|
UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
|
|
|
|
|
|
+ // Synchronize io.KeyMods and io.KeyXXX values.
|
|
|
+ // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
|
|
|
+ // - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
|
|
|
+ // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
|
|
|
+ io.KeyMods = GetMergedModsFromKeys();
|
|
|
+ io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
|
|
|
+ io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
|
|
|
+ io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
|
|
|
+ io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
|
|
|
+
|
|
|
// Clear gamepad data if disabled
|
|
|
if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
|
|
|
for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)
|
|
@@ -4519,13 +4535,58 @@ static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
|
|
|
IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
|
|
|
g.WheelingWindow = window;
|
|
|
g.WheelingWindowRefMousePos = g.IO.MousePos;
|
|
|
+ if (window == NULL)
|
|
|
+ {
|
|
|
+ g.WheelingWindowStartFrame = -1;
|
|
|
+ g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
|
|
|
+{
|
|
|
+ // For each axis, find window in the hierarchy that may want to use scrolling
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiWindow* windows[2] = { NULL, NULL };
|
|
|
+ for (int axis = 0; axis < 2; axis++)
|
|
|
+ if (wheel[axis] != 0.0f)
|
|
|
+ for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
|
|
|
+ {
|
|
|
+ // Bubble up into parent window if:
|
|
|
+ // - a child window doesn't allow any scrolling.
|
|
|
+ // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
|
|
|
+ //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
|
|
|
+ const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
|
|
|
+ const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
|
|
|
+ //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
|
|
|
+ if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
|
|
|
+ break; // select this window
|
|
|
+ }
|
|
|
+ if (windows[0] == NULL && windows[1] == NULL)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ // If there's only one window or only one axis then there's no ambiguity
|
|
|
+ if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
|
|
|
+ return windows[1] ? windows[1] : windows[0];
|
|
|
+
|
|
|
+ // If candidate are different windows we need to decide which one to prioritize
|
|
|
+ // - First frame: only find a winner if one axis is zero.
|
|
|
+ // - Subsequent frames: only find a winner when one is more than the other.
|
|
|
+ if (g.WheelingWindowStartFrame == -1)
|
|
|
+ g.WheelingWindowStartFrame = g.FrameCount;
|
|
|
+ if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
|
|
|
+ {
|
|
|
+ g.WheelingWindowWheelRemainder = wheel;
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
|
|
|
}
|
|
|
|
|
|
void ImGui::UpdateMouseWheel()
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
|
|
|
- // Reset the locked window if we move the mouse or after the timer elapses
|
|
|
+ // Reset the locked window if we move the mouse or after the timer elapses.
|
|
|
+ // FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
|
|
|
if (g.WheelingWindow != NULL)
|
|
|
{
|
|
|
g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
|
|
@@ -4538,8 +4599,6 @@ void ImGui::UpdateMouseWheel()
|
|
|
ImVec2 wheel;
|
|
|
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;
|
|
|
|
|
|
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
|
|
|
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
|
|
@@ -4577,39 +4636,41 @@ void ImGui::UpdateMouseWheel()
|
|
|
wheel.y = 0.0f;
|
|
|
}
|
|
|
|
|
|
- // Vertical Mouse Wheel scrolling
|
|
|
- // Bubble up into parent window if:
|
|
|
- // - a child window doesn't allow any scrolling.
|
|
|
- // - a child window doesn't need scrolling because it is already at the edge for the direction we are going in.
|
|
|
- // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
|
|
|
- if (wheel.y != 0.0f)
|
|
|
- {
|
|
|
- ImGuiWindow* window = mouse_window;
|
|
|
- while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
|
|
|
- window = window->ParentWindow;
|
|
|
- if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
|
|
|
- {
|
|
|
- LockWheelingWindow(mouse_window, wheel.y);
|
|
|
- float max_step = window->InnerRect.GetHeight() * 0.67f;
|
|
|
- float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
|
|
|
- SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
|
|
|
- }
|
|
|
- }
|
|
|
+ // Maintain a rough average of moving magnitude on both axises
|
|
|
+ // FIXME: should by based on wall clock time rather than frame-counter
|
|
|
+ g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
|
|
|
+ g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
|
|
|
|
|
|
- // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
|
|
|
- if (wheel.x != 0.0f)
|
|
|
- {
|
|
|
- ImGuiWindow* window = mouse_window;
|
|
|
- while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
|
|
|
- window = window->ParentWindow;
|
|
|
+ // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
|
|
|
+ wheel += g.WheelingWindowWheelRemainder;
|
|
|
+ g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
|
|
|
+ if (wheel.x == 0.0f && wheel.y == 0.0f)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // Mouse wheel scrolling: find target and apply
|
|
|
+ // - don't renew lock if axis doesn't apply on the window.
|
|
|
+ // - select a main axis when both axises are being moved.
|
|
|
+ if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
|
|
|
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
|
|
|
{
|
|
|
- LockWheelingWindow(mouse_window, wheel.x);
|
|
|
- float max_step = window->InnerRect.GetWidth() * 0.67f;
|
|
|
- float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
|
|
|
- SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
|
|
|
+ bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
|
|
|
+ if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
|
|
|
+ do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
|
|
|
+ if (do_scroll[ImGuiAxis_X])
|
|
|
+ {
|
|
|
+ LockWheelingWindow(window, wheel.x);
|
|
|
+ float max_step = window->InnerRect.GetWidth() * 0.67f;
|
|
|
+ float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
|
|
|
+ SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
|
|
|
+ }
|
|
|
+ if (do_scroll[ImGuiAxis_Y])
|
|
|
+ {
|
|
|
+ LockWheelingWindow(window, wheel.y);
|
|
|
+ float max_step = window->InnerRect.GetHeight() * 0.67f;
|
|
|
+ float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
|
|
|
+ SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
|
|
@@ -8566,9 +8627,12 @@ const char* ImGui::GetKeyName(ImGuiKey key)
|
|
|
return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
|
|
|
}
|
|
|
|
|
|
+// ImGuiMod_Shortcut is translated to either Ctrl or Super.
|
|
|
void ImGui::GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
+ if (key_chord & ImGuiMod_Shortcut)
|
|
|
+ key_chord = ConvertShortcutMod(key_chord);
|
|
|
ImFormatString(out_buf, (size_t)out_buf_size, "%s%s%s%s%s",
|
|
|
(key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
|
|
|
(key_chord & ImGuiMod_Shift) ? "Shift+" : "",
|
|
@@ -8644,6 +8708,8 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
|
|
|
ImGuiKeyRoutingData* routing_data;
|
|
|
+ if (key_chord & ImGuiMod_Shortcut)
|
|
|
+ key_chord = ConvertShortcutMod(key_chord);
|
|
|
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
|
|
|
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
|
|
|
if (key == ImGuiKey_None)
|
|
@@ -8761,7 +8827,7 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI
|
|
|
bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
|
|
|
{
|
|
|
const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
|
|
|
- ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
|
|
|
+ ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
|
|
|
return routing_data->RoutingCurr == routing_id;
|
|
|
}
|
|
|
|
|
@@ -9016,7 +9082,7 @@ static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("%s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f, %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; }
|
|
|
if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; }
|
|
|
- if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f, %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; }
|
|
|
+ if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.3f, %.3f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; }
|
|
|
if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("%s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; }
|
|
|
if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
|
|
|
if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
|
|
@@ -9092,15 +9158,6 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
|
|
|
key_changed = true;
|
|
|
key_changed_mask.SetBit(key_data_index);
|
|
|
|
|
|
- if (key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super)
|
|
|
- {
|
|
|
- if (key == ImGuiMod_Ctrl) { io.KeyCtrl = key_data->Down; }
|
|
|
- if (key == ImGuiMod_Shift) { io.KeyShift = key_data->Down; }
|
|
|
- if (key == ImGuiMod_Alt) { io.KeyAlt = key_data->Down; }
|
|
|
- if (key == ImGuiMod_Super) { io.KeySuper = key_data->Down; }
|
|
|
- io.KeyMods = GetMergedModsFromBools();
|
|
|
- }
|
|
|
-
|
|
|
// Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends
|
|
|
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
|
|
|
io.KeysDown[key_data_index] = key_data->Down;
|
|
@@ -9257,12 +9314,14 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags
|
|
|
if (!SetShortcutRouting(key_chord, owner_id, flags))
|
|
|
return false;
|
|
|
|
|
|
- ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
|
|
|
+ if (key_chord & ImGuiMod_Shortcut)
|
|
|
+ key_chord = ConvertShortcutMod(key_chord);
|
|
|
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
|
|
|
if (g.IO.KeyMods != mods)
|
|
|
return false;
|
|
|
|
|
|
// Special storage location for mods
|
|
|
+ ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
|
|
|
if (key == ImGuiKey_None)
|
|
|
key = ConvertSingleModFlagToKey(mods);
|
|
|
|
|
@@ -9418,9 +9477,9 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
|
|
|
// One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
|
|
|
// It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
|
|
|
// send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
|
|
|
- // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
|
|
|
+ // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
|
|
|
// while still correctly asserting on mid-frame key press events.
|
|
|
- const ImGuiKeyChord key_mods = GetMergedModsFromBools();
|
|
|
+ const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
|
|
|
IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
|
|
|
IM_UNUSED(key_mods);
|
|
|
|
|
@@ -18416,6 +18475,55 @@ static int IMGUI_CDECL ViewportComparerByFrontMostStampCount(const void* lhs, co
|
|
|
return b->LastFrontMostStampCount - a->LastFrontMostStampCount;
|
|
|
}
|
|
|
|
|
|
+// Draw an arbitrary US keyboard layout to visualize translated keys
|
|
|
+void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
|
|
|
+{
|
|
|
+ const ImVec2 key_size = ImVec2(35.0f, 35.0f);
|
|
|
+ const float key_rounding = 3.0f;
|
|
|
+ const ImVec2 key_face_size = ImVec2(25.0f, 25.0f);
|
|
|
+ const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f);
|
|
|
+ const float key_face_rounding = 2.0f;
|
|
|
+ const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f);
|
|
|
+ const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
|
|
|
+ const float key_row_offset = 9.0f;
|
|
|
+
|
|
|
+ ImVec2 board_min = GetCursorScreenPos();
|
|
|
+ ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);
|
|
|
+ ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
|
|
|
+
|
|
|
+ struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
|
|
|
+ const KeyLayoutData keys_to_display[] =
|
|
|
+ {
|
|
|
+ { 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
|
|
|
+ { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
|
|
|
+ { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
|
|
|
+ };
|
|
|
+
|
|
|
+ // Elements rendered manually via ImDrawList API are not clipped automatically.
|
|
|
+ // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
|
|
|
+ Dummy(board_max - board_min);
|
|
|
+ if (!IsItemVisible())
|
|
|
+ return;
|
|
|
+ draw_list->PushClipRect(board_min, board_max, true);
|
|
|
+ for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
|
|
|
+ {
|
|
|
+ const KeyLayoutData* key_data = &keys_to_display[n];
|
|
|
+ ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);
|
|
|
+ ImVec2 key_max = key_min + key_size;
|
|
|
+ draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
|
|
|
+ draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
|
|
|
+ ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
|
|
|
+ ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
|
|
|
+ draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
|
|
|
+ draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
|
|
|
+ ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
|
|
|
+ draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
|
|
|
+ if (ImGui::IsKeyDown(key_data->Key))
|
|
|
+ draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
|
|
|
+ }
|
|
|
+ draw_list->PopClipRect();
|
|
|
+}
|
|
|
+
|
|
|
// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
|
|
|
void ImGui::DebugTextEncoding(const char* str)
|
|
|
{
|
|
@@ -18884,41 +18992,95 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
|
|
TreePop();
|
|
|
}
|
|
|
|
|
|
- if (TreeNode("Key Owners & Shortcut Routing"))
|
|
|
+ if (TreeNode("Inputs"))
|
|
|
{
|
|
|
- TextUnformatted("Key Owners:");
|
|
|
- if (BeginListBox("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8)))
|
|
|
+ Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
|
|
|
{
|
|
|
- for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
|
|
|
+ // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends.
|
|
|
+ // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes.
|
|
|
+ Indent();
|
|
|
+#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
|
|
|
+ struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
|
|
|
+#else
|
|
|
+ struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
|
|
|
+ //Text("Legacy raw:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } }
|
|
|
+#endif
|
|
|
+ Text("Keys down:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
|
|
|
+ Text("Keys pressed:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyPressed(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
|
|
|
+ Text("Keys released:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
|
|
|
+ Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
|
|
|
+ Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
|
|
|
+ DebugRenderKeyboardPreview(GetWindowDrawList());
|
|
|
+ Unindent();
|
|
|
+ }
|
|
|
+
|
|
|
+ Text("MOUSE STATE");
|
|
|
+ {
|
|
|
+ Indent();
|
|
|
+ if (IsMousePosValid())
|
|
|
+ Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
|
|
|
+ else
|
|
|
+ Text("Mouse pos: <INVALID>");
|
|
|
+ Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
|
|
|
+ int count = IM_ARRAYSIZE(io.MouseDown);
|
|
|
+ Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
|
|
|
+ Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
|
|
|
+ Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
|
|
|
+ Text("Mouse wheel: %.1f", io.MouseWheel);
|
|
|
+ Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
|
|
|
+ Unindent();
|
|
|
+ }
|
|
|
+
|
|
|
+ Text("MOUSE WHEELING");
|
|
|
+ {
|
|
|
+ Indent();
|
|
|
+ Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
|
|
|
+ Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
|
|
|
+ Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : "<none>");
|
|
|
+ Unindent();
|
|
|
+ }
|
|
|
+
|
|
|
+ Text("KEY OWNERS");
|
|
|
+ {
|
|
|
+ Indent();
|
|
|
+ if (BeginListBox("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6)))
|
|
|
{
|
|
|
- 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);
|
|
|
+ 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();
|
|
|
}
|
|
|
- EndListBox();
|
|
|
+ Unindent();
|
|
|
}
|
|
|
- TextUnformatted("Shortcut Routing:");
|
|
|
- if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8)))
|
|
|
+ Text("SHORTCUT ROUTING");
|
|
|
{
|
|
|
- for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
|
|
|
+ Indent();
|
|
|
+ if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6)))
|
|
|
{
|
|
|
- ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
|
|
|
- for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
|
|
|
+ for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 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;
|
|
|
+ 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();
|
|
|
}
|
|
|
- EndListBox();
|
|
|
+ Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
|
|
|
+ Unindent();
|
|
|
}
|
|
|
- Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
|
|
|
TreePop();
|
|
|
}
|
|
|
|