diff --git a/README.md b/README.md index 639ac5c..c55ab19 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# zgui v0.9.4 - dear imgui bindings +# zgui v0.9.5 - dear imgui bindings Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. For a test application please see [here](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu). diff --git a/libs/imgui/LICENSE.txt b/libs/imgui/LICENSE.txt index 4023e0c..fb715bd 100644 --- a/libs/imgui/LICENSE.txt +++ b/libs/imgui/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2022 Omar Cornut +Copyright (c) 2014-2023 Omar Cornut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/libs/imgui/backends/imgui_impl_glfw.cpp b/libs/imgui/backends/imgui_impl_glfw.cpp index 5daf392..4047f67 100644 --- a/libs/imgui/backends/imgui_impl_glfw.cpp +++ b/libs/imgui/backends/imgui_impl_glfw.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty. // 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908) // 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -282,26 +283,16 @@ static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) } } -static int ImGui_ImplGlfw_KeyToModifier(int key) -{ - if (key == GLFW_KEY_LEFT_CONTROL || key == GLFW_KEY_RIGHT_CONTROL) - return GLFW_MOD_CONTROL; - if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT) - return GLFW_MOD_SHIFT; - if (key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT) - return GLFW_MOD_ALT; - if (key == GLFW_KEY_LEFT_SUPER || key == GLFW_KEY_RIGHT_SUPER) - return GLFW_MOD_SUPER; - return 0; -} - -static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods) +// X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW +// See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630 +static void ImGui_ImplGlfw_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); - io.AddKeyEvent(ImGuiMod_Ctrl, (mods & GLFW_MOD_CONTROL) != 0); - io.AddKeyEvent(ImGuiMod_Shift, (mods & GLFW_MOD_SHIFT) != 0); - io.AddKeyEvent(ImGuiMod_Alt, (mods & GLFW_MOD_ALT) != 0); - io.AddKeyEvent(ImGuiMod_Super, (mods & GLFW_MOD_SUPER) != 0); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + io.AddKeyEvent(ImGuiMod_Ctrl, (glfwGetKey(bd->Window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Alt, (glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); } void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) @@ -310,7 +301,7 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti if (bd->PrevUserCallbackMousebutton != nullptr && window == bd->Window) bd->PrevUserCallbackMousebutton(window, button, action, mods); - ImGui_ImplGlfw_UpdateKeyModifiers(mods); + ImGui_ImplGlfw_UpdateKeyModifiers(); ImGuiIO& io = ImGui::GetIO(); if (button >= 0 && button < ImGuiMouseButton_COUNT) @@ -369,10 +360,7 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i if (action != GLFW_PRESS && action != GLFW_RELEASE) return; - // Workaround: X11 does not include current pressed/released modifier key in 'mods' flags. https://github.com/glfw/glfw/issues/1630 - if (int keycode_to_mod = ImGui_ImplGlfw_KeyToModifier(keycode)) - mods = (action == GLFW_PRESS) ? (mods | keycode_to_mod) : (mods & ~keycode_to_mod); - ImGui_ImplGlfw_UpdateKeyModifiers(mods); + ImGui_ImplGlfw_UpdateKeyModifiers(); keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); diff --git a/libs/imgui/imgui.cpp b/libs/imgui/imgui.cpp index d179039..3faca98 100644 --- a/libs/imgui/imgui.cpp +++ b/libs/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.1 +// dear imgui, v1.89.2 // (main code and documentation) // Help: @@ -69,6 +69,7 @@ CODE // [SECTION] ImGuiListClipper // [SECTION] STYLING // [SECTION] RENDER HELPERS +// [SECTION] INITIALIZATION, SHUTDOWN // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) // [SECTION] INPUTS // [SECTION] ERROR CHECKING @@ -395,6 +396,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. @@ -1011,14 +1017,17 @@ static void ErrorCheckEndFrameSanityChecks(); static void UpdateDebugToolItemPicker(); static void UpdateDebugToolStackQueries(); -// Misc -static void UpdateSettings(); +// Inputs static void UpdateKeyboardInputs(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); +static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt); + +// Misc +static void UpdateSettings(); static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); -static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); +static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); @@ -1343,6 +1352,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 @@ -2871,11 +2881,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; } @@ -3387,6 +3400,215 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso } } +//----------------------------------------------------------------------------- +// [SECTION] INITIALIZATION, SHUTDOWN +//----------------------------------------------------------------------------- + +// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself +// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module +ImGuiContext* ImGui::GetCurrentContext() +{ + return GImGui; +} + +void ImGui::SetCurrentContext(ImGuiContext* ctx) +{ +#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC + IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. +#else + GImGui = ctx; +#endif +} + +void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) +{ + GImAllocatorAllocFunc = alloc_func; + GImAllocatorFreeFunc = free_func; + GImAllocatorUserData = user_data; +} + +// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) +void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) +{ + *p_alloc_func = GImAllocatorAllocFunc; + *p_free_func = GImAllocatorFreeFunc; + *p_user_data = GImAllocatorUserData; +} + +ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) +{ + ImGuiContext* prev_ctx = GetCurrentContext(); + ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); + SetCurrentContext(ctx); + Initialize(); + if (prev_ctx != NULL) + SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one. + return ctx; +} + +void ImGui::DestroyContext(ImGuiContext* ctx) +{ + ImGuiContext* prev_ctx = GetCurrentContext(); + if (ctx == NULL) //-V1051 + ctx = prev_ctx; + SetCurrentContext(ctx); + Shutdown(); + SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL); + IM_DELETE(ctx); +} + +// IMPORTANT: ###xxx suffixes must be same in ALL languages +static const ImGuiLocEntry GLocalizationEntriesEnUS[] = +{ + { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, + { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, + { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, + { ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" }, + { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" }, + { ImGuiLocKey_WindowingPopup, "(Popup)" }, + { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, +}; + +void ImGui::Initialize() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(!g.Initialized && !g.SettingsLoaded); + + // Add .ini handle for ImGuiWindow and ImGuiTable types + { + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = "Window"; + ini_handler.TypeHash = ImHashStr("Window"); + ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; + ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; + ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; + ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; + ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; + AddSettingsHandler(&ini_handler); + } + TableSettingsAddSettingsHandler(); + + // Setup default localization table + LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS)); + + // Create default viewport + ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); + g.Viewports.push_back(viewport); + g.TempBuffer.resize(1024 * 3 + 1, 0); + +#ifdef IMGUI_HAS_DOCK +#endif + + g.Initialized = true; +} + +// This function is merely here to free heap allocations. +void ImGui::Shutdown() +{ + // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) + ImGuiContext& g = *GImGui; + if (g.IO.Fonts && g.FontAtlasOwnedByContext) + { + g.IO.Fonts->Locked = false; + IM_DELETE(g.IO.Fonts); + } + g.IO.Fonts = NULL; + g.DrawListSharedData.TempBuffer.clear(); + + // Cleanup of other data are conditional on actually having initialized Dear ImGui. + if (!g.Initialized) + return; + + // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) + if (g.SettingsLoaded && g.IO.IniFilename != NULL) + SaveIniSettingsToDisk(g.IO.IniFilename); + + CallContextHooks(&g, ImGuiContextHookType_Shutdown); + + // Clear everything else + g.Windows.clear_delete(); + g.WindowsFocusOrder.clear(); + g.WindowsTempSortBuffer.clear(); + g.CurrentWindow = NULL; + g.CurrentWindowStack.clear(); + g.WindowsById.Clear(); + g.NavWindow = NULL; + 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(); + g.OpenPopupStack.clear(); + g.BeginPopupStack.clear(); + + g.Viewports.clear_delete(); + + g.TabBars.Clear(); + g.CurrentTabBarStack.clear(); + g.ShrinkWidthBuffer.clear(); + + g.ClipperTempData.clear_destruct(); + + g.Tables.Clear(); + g.TablesTempData.clear_destruct(); + g.DrawChannelsTempMergeBuffer.clear(); + + g.ClipboardHandlerData.clear(); + g.MenusIdSubmittedThisFrame.clear(); + g.InputTextState.ClearFreeMemory(); + + g.SettingsWindows.clear(); + g.SettingsHandlers.clear(); + + if (g.LogFile) + { +#ifndef IMGUI_DISABLE_TTY_FUNCTIONS + if (g.LogFile != stdout) +#endif + ImFileClose(g.LogFile); + g.LogFile = NULL; + } + g.LogBuffer.clear(); + g.DebugLogBuf.clear(); + g.DebugLogIndex.clear(); + + g.Initialized = false; +} + +// No specific ordering/dependency support, will see as needed +ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) +{ + ImGuiContext& g = *ctx; + IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); + g.Hooks.push_back(*hook); + g.Hooks.back().HookId = ++g.HookIdNext; + return g.HookIdNext; +} + +// Deferred removal, avoiding issue with changing vector while iterating it +void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) +{ + ImGuiContext& g = *ctx; + IM_ASSERT(hook_id != 0); + for (int n = 0; n < g.Hooks.Size; n++) + if (g.Hooks[n].HookId == hook_id) + g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; +} + +// Call context hooks (used by e.g. test engine) +// We assume a small number of hooks so all stored in same array +void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) +{ + ImGuiContext& g = *ctx; + for (int n = 0; n < g.Hooks.Size; n++) + if (g.Hooks[n].Type == hook_type) + g.Hooks[n].Callback(&g, &g.Hooks[n]); +} + //----------------------------------------------------------------------------- // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) @@ -3833,89 +4055,6 @@ const char* ImGui::GetVersion() return IMGUI_VERSION; } -// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself -// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module -ImGuiContext* ImGui::GetCurrentContext() -{ - return GImGui; -} - -void ImGui::SetCurrentContext(ImGuiContext* ctx) -{ -#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC - IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. -#else - GImGui = ctx; -#endif -} - -void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) -{ - GImAllocatorAllocFunc = alloc_func; - GImAllocatorFreeFunc = free_func; - GImAllocatorUserData = user_data; -} - -// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) -void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) -{ - *p_alloc_func = GImAllocatorAllocFunc; - *p_free_func = GImAllocatorFreeFunc; - *p_user_data = GImAllocatorUserData; -} - -ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) -{ - ImGuiContext* prev_ctx = GetCurrentContext(); - ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); - SetCurrentContext(ctx); - Initialize(); - if (prev_ctx != NULL) - SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one. - return ctx; -} - -void ImGui::DestroyContext(ImGuiContext* ctx) -{ - ImGuiContext* prev_ctx = GetCurrentContext(); - if (ctx == NULL) //-V1051 - ctx = prev_ctx; - SetCurrentContext(ctx); - Shutdown(); - SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL); - IM_DELETE(ctx); -} - -// No specific ordering/dependency support, will see as needed -ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) -{ - ImGuiContext& g = *ctx; - IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); - g.Hooks.push_back(*hook); - g.Hooks.back().HookId = ++g.HookIdNext; - return g.HookIdNext; -} - -// Deferred removal, avoiding issue with changing vector while iterating it -void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) -{ - ImGuiContext& g = *ctx; - IM_ASSERT(hook_id != 0); - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].HookId == hook_id) - g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; -} - -// Call context hooks (used by e.g. test engine) -// We assume a small number of hooks so all stored in same array -void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) -{ - ImGuiContext& g = *ctx; - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].Type == hook_type) - g.Hooks[n].Callback(&g, &g.Hooks[n]); -} - ImGuiIO& ImGui::GetIO() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); @@ -4109,341 +4248,6 @@ static bool IsWindowActiveAndVisible(ImGuiWindow* window) return (window->Active) && (!window->Hidden); } -static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value) -{ - IM_ASSERT(ImGui::IsAliasKey(key)); - ImGuiKeyData* key_data = ImGui::GetKeyData(key); - key_data->Down = v; - 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() -{ - 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; -} - -static void ImGui::UpdateKeyboardInputs() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - // Import legacy keys or verify they are not used -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - if (io.BackendUsingLegacyKeyArrays == 0) - { - // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally. - for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++) - IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); - } - else - { - if (g.FrameCount == 0) - for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) - IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!"); - - // Build reverse KeyMap (Named -> Legacy) - for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) - if (io.KeyMap[n] != -1) - { - IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n])); - io.KeyMap[io.KeyMap[n]] = n; - } - - // Import legacy keys into new ones - for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) - if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1) - { - const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n); - IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key)); - io.KeysData[key].Down = io.KeysDown[n]; - if (key != n) - io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends - io.BackendUsingLegacyKeyArrays = 1; - } - if (io.BackendUsingLegacyKeyArrays == 1) - { - GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl; - GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift; - GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt; - GetKeyData(ImGuiMod_Super)->Down = io.KeySuper; - } - } - -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active) - { - #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0) - #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0) - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown); - MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow); - MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp); - MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown); - #undef NAV_MAP_KEY - } -#endif - -#endif - - // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools, update aliases - io.KeyMods = GetMergedModsFromBools(); - 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); - - // Clear gamepad data if disabled - if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) - for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++) - { - io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false; - io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f; - } - - // Update keys - 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 - } - - UpdateKeyRoutingTable(&g.KeysRoutingTable); -} - -static void ImGui::UpdateMouseInputs() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) - if (IsMousePosValid(&io.MousePos)) - io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); - - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta - if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) - io.MouseDelta = io.MousePos - io.MousePosPrev; - else - io.MouseDelta = ImVec2(0.0f, 0.0f); - - // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. - if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) - g.NavDisableMouseHover = false; - - io.MousePosPrev = io.MousePos; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; - io.MouseClickedCount[i] = 0; // Will be filled below - io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; - io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; - io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; - if (io.MouseClicked[i]) - { - bool is_repeated_click = false; - if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) - { - ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) - is_repeated_click = true; - } - if (is_repeated_click) - io.MouseClickedLastCount[i]++; - else - io.MouseClickedLastCount[i] = 1; - io.MouseClickedTime[i] = g.Time; - io.MouseClickedPos[i] = io.MousePos; - io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; - io.MouseDragMaxDistanceSqr[i] = 0.0f; - } - else if (io.MouseDown[i]) - { - // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; - io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); - } - - // We provide io.MouseDoubleClicked[] as a legacy service - io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); - - // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation - if (io.MouseClicked[i]) - g.NavDisableMouseHover = false; - } -} - -static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount) -{ - ImGuiContext& g = *GImGui; - if (window) - g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER); - else - g.WheelingWindowReleaseTimer = 0.0f; - if (g.WheelingWindow == window) - return; - IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL"); - g.WheelingWindow = window; - g.WheelingWindowRefMousePos = g.IO.MousePos; -} - -void ImGui::UpdateMouseWheel() -{ - ImGuiContext& g = *GImGui; - - // Reset the locked window if we move the mouse or after the timer elapses - if (g.WheelingWindow != NULL) - { - g.WheelingWindowReleaseTimer -= g.IO.DeltaTime; - if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) - g.WheelingWindowReleaseTimer = 0.0f; - if (g.WheelingWindowReleaseTimer <= 0.0f) - LockWheelingWindow(NULL, 0.0f); - } - - 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; - if (!mouse_window || mouse_window->Collapsed) - return; - - // Zoom / Scale window - // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. - if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) - { - LockWheelingWindow(mouse_window, wheel.y); - ImGuiWindow* window = mouse_window; - const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); - const float scale = new_font_scale / window->FontWindowScale; - window->FontWindowScale = new_font_scale; - if (window == window->RootWindow) - { - const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; - SetWindowPos(window, window->Pos + offset, 0); - window->Size = ImFloor(window->Size * scale); - window->SizeFull = ImFloor(window->SizeFull * scale); - } - return; - } - if (g.IO.KeyCtrl) - return; - - // Mouse wheel scrolling - // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead - // (we avoid doing it on OSX as it the OS input layer handles this already) - const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; - if (swap_axis) - { - wheel.x = wheel.y; - 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); - } - } - - // 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; - 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); - } - } -} - // 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) void ImGui::UpdateHoveredWindowAndCaptureFlags() { @@ -4773,128 +4577,6 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -// IMPORTANT: ###xxx suffixes must be same in ALL languages -static const ImGuiLocEntry GLocalizationEntriesEnUS[] = -{ - { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, - { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, - { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, - { ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" }, - { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" }, - { ImGuiLocKey_WindowingPopup, "(Popup)" }, - { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, -}; - -void ImGui::Initialize() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(!g.Initialized && !g.SettingsLoaded); - - // Add .ini handle for ImGuiWindow and ImGuiTable types - { - ImGuiSettingsHandler ini_handler; - ini_handler.TypeName = "Window"; - ini_handler.TypeHash = ImHashStr("Window"); - ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; - ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; - ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; - ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; - ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; - AddSettingsHandler(&ini_handler); - } - TableSettingsAddSettingsHandler(); - - // Setup default localization table - LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS)); - - // Create default viewport - ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); - g.Viewports.push_back(viewport); - g.TempBuffer.resize(1024 * 3 + 1, 0); - -#ifdef IMGUI_HAS_DOCK -#endif - - g.Initialized = true; -} - -// This function is merely here to free heap allocations. -void ImGui::Shutdown() -{ - // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) - ImGuiContext& g = *GImGui; - if (g.IO.Fonts && g.FontAtlasOwnedByContext) - { - g.IO.Fonts->Locked = false; - IM_DELETE(g.IO.Fonts); - } - g.IO.Fonts = NULL; - g.DrawListSharedData.TempBuffer.clear(); - - // Cleanup of other data are conditional on actually having initialized Dear ImGui. - if (!g.Initialized) - return; - - // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) - if (g.SettingsLoaded && g.IO.IniFilename != NULL) - SaveIniSettingsToDisk(g.IO.IniFilename); - - CallContextHooks(&g, ImGuiContextHookType_Shutdown); - - // Clear everything else - g.Windows.clear_delete(); - g.WindowsFocusOrder.clear(); - g.WindowsTempSortBuffer.clear(); - g.CurrentWindow = NULL; - g.CurrentWindowStack.clear(); - g.WindowsById.Clear(); - g.NavWindow = NULL; - 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(); - g.OpenPopupStack.clear(); - g.BeginPopupStack.clear(); - - g.Viewports.clear_delete(); - - g.TabBars.Clear(); - g.CurrentTabBarStack.clear(); - g.ShrinkWidthBuffer.clear(); - - g.ClipperTempData.clear_destruct(); - - g.Tables.Clear(); - g.TablesTempData.clear_destruct(); - g.DrawChannelsTempMergeBuffer.clear(); - - g.ClipboardHandlerData.clear(); - g.MenusIdSubmittedThisFrame.clear(); - g.InputTextState.ClearFreeMemory(); - - g.SettingsWindows.clear(); - g.SettingsHandlers.clear(); - - if (g.LogFile) - { -#ifndef IMGUI_DISABLE_TTY_FUNCTIONS - if (g.LogFile != stdout) -#endif - ImFileClose(g.LogFile); - g.LogFile = NULL; - } - g.LogBuffer.clear(); - g.DebugLogBuf.clear(); - g.DebugLogIndex.clear(); - - g.Initialized = false; -} - // FIXME: Add a more explicit sort order in the window structure. static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) { @@ -5466,6 +5148,12 @@ void ImGui::SetActiveIdUsingAllKeyboardKeys() NavMoveRequestCancel(); } +ImGuiID ImGui::GetItemID() +{ + ImGuiContext& g = *GImGui; + return g.LastItemData.ID; +} + ImVec2 ImGui::GetItemRectMin() { ImGuiContext& g = *GImGui; @@ -5738,9 +5426,9 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& s if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) { ImGuiWindow* window_for_height = window; - const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight(); new_size = ImMax(new_size, g.Style.WindowMinSize); - new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows + const float minimum_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f); + new_size.y = ImMax(new_size.y, minimum_height); // Reduce artifacts with very small windows } return new_size; } @@ -5769,9 +5457,10 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); + const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x; + const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y; ImVec2 size_pad = window->WindowPadding * 2.0f; - ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height); + ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars); if (window->Flags & ImGuiWindowFlags_Tooltip) { // Tooltip always resize @@ -5792,8 +5481,8 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) @@ -6003,9 +5692,9 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { ImVec2 nav_resize_dir; if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) - nav_resize_dir = GetKeyVector2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); + nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); if (g.NavInputSource == ImGuiInputSource_Gamepad) - nav_resize_dir = GetKeyVector2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown); + nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown); if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f) { const float NAV_RESIZE_SPEED = 600.0f; @@ -6076,7 +5765,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) // Draw background and borders // Draw and handle scrollbars -void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) +void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; @@ -6141,7 +5830,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar Scrollbar(ImGuiAxis_Y); // Render resize grips (after their input handling so we don't have a frame of latency) - if (!(flags & ImGuiWindowFlags_NoResize)) + if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize)) { for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { @@ -6155,7 +5844,8 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } // Borders - RenderWindowOuterBorders(window); + if (handle_borders_and_resize_grips) + RenderWindowOuterBorders(window); } } @@ -6555,6 +6245,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // SIZE + // Outer Decoration Sizes + // (we need to clear ScrollbarSize immediatly as CalcWindowAutoFitSize() needs it and can be called from other locations). + const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes; + window->DecoOuterSizeX1 = 0.0f; + window->DecoOuterSizeX2 = 0.0f; + window->DecoOuterSizeY1 = window->TitleBarHeight() + window->MenuBarHeight(); + window->DecoOuterSizeY2 = 0.0f; + window->ScrollbarSizes = ImVec2(0.0f, 0.0f); + // Calculate auto-fit size, handle automatic resize const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal); if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) @@ -6593,9 +6292,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull); window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; - // Decoration size - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); - // POSITION // Popup latch its initial position, will position itself when it appears next frame @@ -6702,9 +6398,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (!window->Collapsed) { // When reading the current size we need to read it after size constraints have been applied. - // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again. - ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height); - ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes; + // Intentionally use previous frame values for InnerRect and ScrollbarSizes. + // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet. + ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)); + ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame; ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; @@ -6714,6 +6411,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->ScrollbarX && !window->ScrollbarY) window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); + + // Amend the partially filled window->DecorationXXX values. + window->DecoOuterSizeX2 += window->ScrollbarSizes.x; + window->DecoOuterSizeY2 += window->ScrollbarSizes.y; } // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) @@ -6737,10 +6438,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // - ScrollToRectEx() // - NavUpdatePageUpPageDown() // - Scrollbar() - window->InnerRect.Min.x = window->Pos.x; - window->InnerRect.Min.y = window->Pos.y + decoration_up_height; - window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; - window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; + window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1; + window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1; + window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2; + window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2; // Inner clipping rectangle. // Will extend a little bit outside the normal work region. @@ -6773,6 +6474,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Apply scrolling window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); + window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f; // DRAWING @@ -6802,7 +6504,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Handle title bar, scrollbar, resize grips and resize borders const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); - RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); + const bool handle_borders_and_resize_grips = true; // This exists to facilitate merge with 'docking' branch. + RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); if (render_decorations_in_parent) window->DrawList = &window->DrawListInst; @@ -6817,8 +6520,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // - BeginTabBar() for right-most edge const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); - const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); - const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); + const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2))); + const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2))); window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; @@ -6829,21 +6532,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. // Used by: // - Mouse wheel scrolling + many other things - window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; - window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height; - window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); - window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); + window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1; + window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1; + window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2))); + window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2))); // Setup drawing context // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) - window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; + window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x; window->DC.GroupOffset.x = 0.0f; window->DC.ColumnsOffset.x = 0.0f; // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. - double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DC.ColumnsOffset.x; - double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + decoration_up_height; + double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x; + double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1; window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); window->DC.CursorPos = window->DC.CursorStartPos; @@ -7855,25 +7558,65 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) //----------------------------------------------------------------------------- // [SECTION] INPUTS //----------------------------------------------------------------------------- - -// Test if mouse cursor is hovering given rectangle -// NB- Rectangle is clipped by our current clip setting -// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) -{ - ImGuiContext& g = *GImGui; - - // Clip - ImRect rect_clipped(r_min, r_max); - if (clip) - rect_clipped.ClipWith(g.CurrentWindow->ClipRect); - - // Expand for touch input - const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - if (!rect_for_touch.Contains(g.IO.MousePos)) - return false; - return true; -} +// - GetKeyData() [Internal] +// - GetKeyIndex() [Internal] +// - GetKeyName() +// - GetKeyChordName() [Internal] +// - CalcTypematicRepeatAmount() [Internal] +// - GetTypematicRepeatRate() [Internal] +// - GetKeyPressedAmount() [Internal] +// - GetKeyMagnitude2d() [Internal] +//----------------------------------------------------------------------------- +// - UpdateKeyRoutingTable() [Internal] +// - GetRoutingIdFromOwnerId() [Internal] +// - GetShortcutRoutingData() [Internal] +// - CalcRoutingScore() [Internal] +// - SetShortcutRouting() [Internal] +// - TestShortcutRouting() [Internal] +//----------------------------------------------------------------------------- +// - IsKeyDown() +// - IsKeyPressed() +// - IsKeyReleased() +//----------------------------------------------------------------------------- +// - IsMouseDown() +// - IsMouseClicked() +// - IsMouseReleased() +// - IsMouseDoubleClicked() +// - GetMouseClickedCount() +// - IsMouseHoveringRect() [Internal] +// - IsMouseDragPastThreshold() [Internal] +// - IsMouseDragging() +// - GetMousePos() +// - GetMousePosOnOpeningCurrentPopup() +// - IsMousePosValid() +// - IsAnyMouseDown() +// - GetMouseDragDelta() +// - ResetMouseDragDelta() +// - GetMouseCursor() +// - SetMouseCursor() +//----------------------------------------------------------------------------- +// - UpdateAliasKey() +// - GetMergedModsFromKeys() +// - UpdateKeyboardInputs() +// - UpdateMouseInputs() +//----------------------------------------------------------------------------- +// - LockWheelingWindow [Internal] +// - FindBestWheelingWindow [Internal] +// - UpdateMouseWheel() [Internal] +//----------------------------------------------------------------------------- +// - SetNextFrameWantCaptureKeyboard() +// - SetNextFrameWantCaptureMouse() +//----------------------------------------------------------------------------- +// - GetInputSourceName() [Internal] +// - DebugPrintInputEvent() [Internal] +// - UpdateInputEvents() [Internal] +//----------------------------------------------------------------------------- +// - GetKeyOwner() [Internal] +// - TestKeyOwner() [Internal] +// - SetKeyOwner() [Internal] +// - SetItemKeyOwner() [Internal] +// - Shortcut() [Internal] +//----------------------------------------------------------------------------- ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) { @@ -7956,9 +7699,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+" : "", @@ -8009,13 +7755,52 @@ int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_ra } // Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values). -ImVec2 ImGui::GetKeyVector2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down) +ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down) { return ImVec2( GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue, GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue); } +// 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 ImGui::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 +} + // owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope(). static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id) { @@ -8034,6 +7819,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) @@ -8151,7 +7938,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; } @@ -8286,6 +8073,25 @@ int ImGui::GetMouseClickedCount(ImGuiMouseButton button) return g.IO.MouseClickedCount[button]; } +// Test if mouse cursor is hovering given rectangle +// NB- Rectangle is clipped by our current clip setting +// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) +bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) +{ + ImGuiContext& g = *GImGui; + + // Clip + ImRect rect_clipped(r_min, r_max); + if (clip) + rect_clipped.ClipWith(g.CurrentWindow->ClipRect); + + // Expand for touch input + const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); + if (!rect_for_touch.Contains(g.IO.MousePos)) + return false; + return true; +} + // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. // [Internal] This doesn't test if the button is pressed bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) @@ -8382,6 +8188,354 @@ void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) g.MouseCursor = cursor_type; } +static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value) +{ + IM_ASSERT(ImGui::IsAliasKey(key)); + ImGuiKeyData* key_data = ImGui::GetKeyData(key); + key_data->Down = v; + key_data->AnalogValue = analog_value; +} + +// [Internal] Do not use directly +static ImGuiKeyChord GetMergedModsFromKeys() +{ + 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() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + + // Import legacy keys or verify they are not used +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + if (io.BackendUsingLegacyKeyArrays == 0) + { + // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally. + for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++) + IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); + } + else + { + if (g.FrameCount == 0) + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!"); + + // Build reverse KeyMap (Named -> Legacy) + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) + if (io.KeyMap[n] != -1) + { + IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n])); + io.KeyMap[io.KeyMap[n]] = n; + } + + // Import legacy keys into new ones + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1) + { + const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n); + IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key)); + io.KeysData[key].Down = io.KeysDown[n]; + if (key != n) + io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends + io.BackendUsingLegacyKeyArrays = 1; + } + if (io.BackendUsingLegacyKeyArrays == 1) + { + GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl; + GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift; + GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt; + GetKeyData(ImGuiMod_Super)->Down = io.KeySuper; + } + } + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active) + { + #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0) + #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0) + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown); + MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow); + MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown); + #undef NAV_MAP_KEY + } +#endif +#endif + + // 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++) + { + io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false; + io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f; + } + + // Update keys + 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 + } + + UpdateKeyRoutingTable(&g.KeysRoutingTable); +} + +static void ImGui::UpdateMouseInputs() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + + // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) + if (IsMousePosValid(&io.MousePos)) + io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); + + // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta + if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) + io.MouseDelta = io.MousePos - io.MousePosPrev; + else + io.MouseDelta = ImVec2(0.0f, 0.0f); + + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. + if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) + g.NavDisableMouseHover = false; + + io.MousePosPrev = io.MousePos; + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + { + io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; + io.MouseClickedCount[i] = 0; // Will be filled below + io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; + io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; + io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; + if (io.MouseClicked[i]) + { + bool is_repeated_click = false; + if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) + { + ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) + is_repeated_click = true; + } + if (is_repeated_click) + io.MouseClickedLastCount[i]++; + else + io.MouseClickedLastCount[i] = 1; + io.MouseClickedTime[i] = g.Time; + io.MouseClickedPos[i] = io.MousePos; + io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; + io.MouseDragMaxDistanceSqr[i] = 0.0f; + } + else if (io.MouseDown[i]) + { + // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold + float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; + io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); + } + + // We provide io.MouseDoubleClicked[] as a legacy service + io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); + + // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (io.MouseClicked[i]) + g.NavDisableMouseHover = false; + } +} + +static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount) +{ + ImGuiContext& g = *GImGui; + if (window) + g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER); + else + g.WheelingWindowReleaseTimer = 0.0f; + if (g.WheelingWindow == window) + return; + 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]; +} + +// Called by NewFrame() +void ImGui::UpdateMouseWheel() +{ + // 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) + ImGuiContext& g = *GImGui; + if (g.WheelingWindow != NULL) + { + g.WheelingWindowReleaseTimer -= g.IO.DeltaTime; + if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) + g.WheelingWindowReleaseTimer = 0.0f; + if (g.WheelingWindowReleaseTimer <= 0.0f) + LockWheelingWindow(NULL, 0.0f); + } + + 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; + + //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); + ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; + if (!mouse_window || mouse_window->Collapsed) + return; + + // Zoom / Scale window + // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. + if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) + { + LockWheelingWindow(mouse_window, wheel.y); + ImGuiWindow* window = mouse_window; + const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); + const float scale = new_font_scale / window->FontWindowScale; + window->FontWindowScale = new_font_scale; + if (window == window->RootWindow) + { + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + SetWindowPos(window, window->Pos + offset, 0); + window->Size = ImFloor(window->Size * scale); + window->SizeFull = ImFloor(window->SizeFull * scale); + } + return; + } + if (g.IO.KeyCtrl) + return; + + // Mouse wheel scrolling + // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead + // (we avoid doing it on OSX as it the OS input layer handles this already) + const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; + if (swap_axis) + { + wheel.x = wheel.y; + wheel.y = 0.0f; + } + + // 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); + + // 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)) + { + 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); + } + } +} + void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard) { ImGuiContext& g = *GImGui; @@ -8406,7 +8560,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_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; } if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } @@ -8477,15 +8631,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; @@ -8630,8 +8775,6 @@ void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) } } -// - Need to decide how to handle shortcut translations for Non-Mac <> Mac -// - Ideas: https://github.com/ocornut/imgui/issues/456#issuecomment-264390864 bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; @@ -8642,16 +8785,18 @@ 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); - if (!IsKeyPressed(key, owner_id, (flags & (ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_)))) + if (!IsKeyPressed(key, owner_id, (flags & (ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateMask_)))) return false; IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function! @@ -8763,9 +8908,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); @@ -8977,7 +9122,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; window->DC.CursorPosPrevLine.y = line_y1; window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line - window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line + window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] @@ -9451,38 +9596,24 @@ static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, fl static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) { ImVec2 scroll = window->Scroll; - if (window->ScrollTarget.x < FLT_MAX) + ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2); + for (int axis = 0; axis < 2; axis++) { - float decoration_total_width = window->ScrollbarSizes.x; - float center_x_ratio = window->ScrollTargetCenterRatio.x; - float scroll_target_x = window->ScrollTarget.x; - if (window->ScrollTargetEdgeSnapDist.x > 0.0f) + if (window->ScrollTarget[axis] < FLT_MAX) { - float snap_x_min = 0.0f; - float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width; - scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); + float center_ratio = window->ScrollTargetCenterRatio[axis]; + float scroll_target = window->ScrollTarget[axis]; + if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f) + { + float snap_min = 0.0f; + float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis]; + scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio); + } + scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]); } - scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width); - } - if (window->ScrollTarget.y < FLT_MAX) - { - float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y; - float center_y_ratio = window->ScrollTargetCenterRatio.y; - float scroll_target_y = window->ScrollTarget.y; - if (window->ScrollTargetEdgeSnapDist.y > 0.0f) - { - float snap_y_min = 0.0f; - float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height; - scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); - } - scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height); - } - scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); - scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); - if (!window->Collapsed && !window->SkipItems) - { - scroll.x = ImMin(scroll.x, window->ScrollMax.x); - scroll.y = ImMin(scroll.y, window->ScrollMax.y); + scroll[axis] = IM_FLOOR(ImMax(scroll[axis], 0.0f)); + if (!window->Collapsed && !window->SkipItems) + scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]); } return scroll; } @@ -9503,8 +9634,11 @@ void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScro ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; - ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); - //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] + ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); + scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x); + scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y); + //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG] + //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG] // Check that only one behavior is selected per axis IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); @@ -9517,31 +9651,31 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui if ((flags & ImGuiScrollFlags_MaskY_) == 0) flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; - const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; - const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; - const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= window_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; - const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= window_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; + const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x; + const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y; + const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; + const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) { - if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x) + if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x) SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); - else if (item_rect.Max.x >= window_rect.Max.x) + else if (item_rect.Max.x >= scroll_rect.Max.x) SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); } else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) { if (can_be_fully_visible_x) - SetScrollFromPosX(window, ImFloor((item_rect.Min.x + item_rect.Max.y) * 0.5f) - window->Pos.x, 0.5f); + SetScrollFromPosX(window, ImFloor((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f); else SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f); } if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) { - if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y) + if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y) SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); - else if (item_rect.Max.y >= window_rect.Max.y) + else if (item_rect.Max.y >= scroll_rect.Max.y) SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); } else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) @@ -9632,7 +9766,7 @@ void ImGui::SetScrollY(float scroll_y) void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) { IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); - window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset + window->ScrollTarget.x = IM_FLOOR(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset window->ScrollTargetCenterRatio.x = center_x_ratio; window->ScrollTargetEdgeSnapDist.x = 0.0f; } @@ -9640,9 +9774,7 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect; - local_y -= decoration_up_height; - window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset + window->ScrollTarget.y = IM_FLOOR(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; window->ScrollTargetEdgeSnapDist.y = 0.0f; } @@ -10973,7 +11105,7 @@ static void ImGui::NavUpdate() // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. if (nav_gamepad_active) { - const ImVec2 scroll_dir = GetKeyVector2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); + const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f; if (scroll_dir.x != 0.0f && window->ScrollbarX) SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor)); @@ -11050,7 +11182,7 @@ void ImGui::NavUpdateCreateMoveRequest() g.NavMoveScrollFlags = ImGuiScrollFlags_None; if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { - const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateNavMove; + const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove; 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; } @@ -11599,9 +11731,9 @@ static void ImGui::NavUpdateWindowing() { ImVec2 nav_move_dir; if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) - nav_move_dir = GetKeyVector2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); + nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); if (g.NavInputSource == ImGuiInputSource_Gamepad) - nav_move_dir = GetKeyVector2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); + nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; @@ -12923,6 +13055,55 @@ static void RenderViewportsThumbnails() ImGui::Dummy(bb_full.GetSize() * SCALE); } +// 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) { @@ -13043,8 +13224,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; } else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } // Note: y1/y2 not always accurate else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } - else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } - else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } + else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); } + else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } IM_ASSERT(0); return ImRect(); } @@ -13321,41 +13502,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)) - { - 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(); + // 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(); } - TextUnformatted("Shortcut Routing:"); - if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8))) + + Text("MOUSE STATE"); { - for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) + Indent(); + if (IsMousePosValid()) + Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); + else + Text("Mouse pos: "); + 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" : ""); + Unindent(); + } + + Text("KEY OWNERS"); + { + Indent(); + if (BeginListBox("##owners", 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; + 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(); + } + Text("SHORTCUT ROUTING"); + { + Indent(); + if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6))) + { + 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); + Unindent(); } - Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); TreePop(); } diff --git a/libs/imgui/imgui.h b/libs/imgui/imgui.h index 03eb0ca..152f4f0 100644 --- a/libs/imgui/imgui.h +++ b/libs/imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.89.1 +// dear imgui, v1.89.2 // (headers) // Help: @@ -22,8 +22,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345') -#define IMGUI_VERSION "1.89.1 WIP" -#define IMGUI_VERSION_NUM 18910 +#define IMGUI_VERSION "1.89.2" +#define IMGUI_VERSION_NUM 18920 #define IMGUI_HAS_TABLE /* @@ -255,8 +255,8 @@ struct ImVec2 float x, y; constexpr ImVec2() : x(0.0f), y(0.0f) { } constexpr ImVec2(float _x, float _y) : x(_x), y(_y) { } - float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. - float& operator[] (size_t idx) { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. #ifdef IM_VEC2_CLASS_EXTRA IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif @@ -419,7 +419,7 @@ namespace ImGui IMGUI_API void PopTextWrapPos(); // Style read access - // - Use the style editor (ShowStyleEditor() function) to interactively see what the colors are) + // - Use the ShowStyleEditor() function to interactively see/edit the colors. IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API @@ -782,6 +782,7 @@ namespace ImGui IMGUI_API int GetColumnsCount(); // Tab Bars, Tabs + // - Note: Tabs are automatically created by the docking system (when in 'docking' branch). Use this to create tab bars/tabs yourself. IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar IMGUI_API void EndTabBar(); // only call EndTabBar() if BeginTabBar() returns true! IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0); // create a Tab. Returns true if the Tab is selected. @@ -845,6 +846,7 @@ namespace ImGui IMGUI_API bool IsAnyItemHovered(); // is any item hovered? IMGUI_API bool IsAnyItemActive(); // is any item active? IMGUI_API bool IsAnyItemFocused(); // is any item focused? + IMGUI_API ImGuiID GetItemID(); // get ID of last item (~~ often same ImGui::GetID(label) beforehand) IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item @@ -881,12 +883,11 @@ namespace ImGui IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); - // Inputs Utilities: Keyboard - // Without IMGUI_DISABLE_OBSOLETE_KEYIO: (legacy support) - // - For 'ImGuiKey key' you can still use your legacy native/user indices according to how your backend/engine stored them in io.KeysDown[]. - // With IMGUI_DISABLE_OBSOLETE_KEYIO: (this is the way forward) - // - Any use of 'ImGuiKey' will assert when key < 512 will be passed, previously reserved as native/user keys indices - // - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined) + // Inputs Utilities: Keyboard/Mouse/Gamepad + // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...). + // - before v1.87, we used ImGuiKey to carry native/user indices as defined by each backends. About use of those legacy ImGuiKey values: + // - without IMGUI_DISABLE_OBSOLETE_KEYIO (legacy support): you can still use your legacy native/user indices (< 512) according to how your backend/engine stored them in io.KeysDown[], but need to cast them to ImGuiKey. + // - with IMGUI_DISABLE_OBSOLETE_KEYIO (this is the way forward): any use of ImGuiKey will assert with key < 512. GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined). IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)? @@ -894,7 +895,7 @@ namespace ImGui IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared. IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call. - // Inputs Utilities: Mouse + // Inputs Utilities: Mouse specific // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') @@ -1011,7 +1012,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) - ImGuiInputTextFlags_EscapeClearsAll = 1 << 20, // Escape key clears content if not empty, and deactivate otherwise (constrast to default behavior of Escape to revert) + ImGuiInputTextFlags_EscapeClearsAll = 1 << 20, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) // Obsolete names (will be removed soon) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1295,7 +1296,7 @@ enum ImGuiDragDropFlags_ { ImGuiDragDropFlags_None = 0, // BeginDragDropSource() flags - ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior. + ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // Disable preview tooltip. By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior. ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disables this behavior so you can still call IsItemHovered() on the source item. ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item. ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. @@ -1347,9 +1348,10 @@ enum ImGuiSortDirection_ ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. }; -// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value) -// All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87) +// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values. +// All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87). // Since >= 1.89 we increased typing (went from int to enum), some legacy code may need a cast to ImGuiKey. +// Read details about the 1.87 and 1.89 transition : https://github.com/ocornut/imgui/issues/4921 enum ImGuiKey : int { // Keyboard @@ -1404,7 +1406,7 @@ enum ImGuiKey : int ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, - // Gamepad (some of those are analog values, 0.0f to 1.0f) // GAME NAVIGATION ACTION + // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION ACTION // (download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets) ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) @@ -1431,7 +1433,7 @@ enum ImGuiKey : int ImGuiKey_GamepadRStickUp, // [Analog] ImGuiKey_GamepadRStickDown, // [Analog] - // Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls) + // Aliases: Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls) // - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API. ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY, @@ -1448,16 +1450,12 @@ enum ImGuiKey : int // In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and // backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user... ImGuiMod_None = 0, - ImGuiMod_Ctrl = 1 << 12, - ImGuiMod_Shift = 1 << 13, + ImGuiMod_Ctrl = 1 << 12, // Ctrl + ImGuiMod_Shift = 1 << 13, // Shift ImGuiMod_Alt = 1 << 14, // Option/Menu ImGuiMod_Super = 1 << 15, // Cmd/Super/Windows - ImGuiMod_Mask_ = 0xF000, -#if defined(__APPLE__) - ImGuiMod_Shortcut = ImGuiMod_Super, -#else - ImGuiMod_Shortcut = ImGuiMod_Ctrl, -#endif + ImGuiMod_Shortcut = 1 << 11, // Alias for Ctrl (non-macOS) _or_ Super (macOS). + ImGuiMod_Mask_ = 0xF800, // 5-bits // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + the io.KeyMap[] array. // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) @@ -1915,7 +1913,7 @@ struct ImGuiIO float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. float HoverDelayNormal; // = 0.30 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. float HoverDelayShort; // = 0.10 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. - void* UserData; // = NULL // Store your own data for retrieval by callbacks. + void* UserData; // = NULL // Store your own data. ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. float FontGlobalScale; // = 1.0f // Global scale all fonts @@ -2028,7 +2026,7 @@ struct ImGuiIO bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows // Other state maintained from data above + IO function calls - ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame() + ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. DOES NOT CONTAINS ImGuiMod_Shortcut which is pretranslated). Read-only, updated by NewFrame() ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) @@ -2812,6 +2810,7 @@ struct ImFontAtlas int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false). bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. + void* UserData; // Store your own atlas related user-data (if e.g. you have multiple font atlas). // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. diff --git a/libs/imgui/imgui_demo.cpp b/libs/imgui/imgui_demo.cpp index 636c7b8..c1b7dbe 100644 --- a/libs/imgui/imgui_demo.cpp +++ b/libs/imgui/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.1 +// dear imgui, v1.89.2 // (demo code) // Help: @@ -3438,11 +3438,14 @@ static void ShowDemoWindowPopups() // and BeginPopupContextItem() will use the last item ID as the popup ID. { const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" }; + static int selected = -1; for (int n = 0; n < 5; n++) { - ImGui::Selectable(names[n]); + if (ImGui::Selectable(names[n], selected == n)) + selected = n; if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id { + selected = n; ImGui::Text("This a popup for \"%s\"!", names[n]); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); @@ -5043,18 +5046,23 @@ static void ShowDemoWindowTables() if (ImGui::TreeNode("Synced instances")) { HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); + + static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings; + ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); + ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit); for (int n = 0; n < 3; n++) { char buf[32]; sprintf(buf, "Synced Table %d", n); bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen); - if (open && ImGui::BeginTable("Table", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings)) + if (open && ImGui::BeginTable("Table", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5))) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); ImGui::TableHeadersRow(); - for (int cell = 0; cell < 9; cell++) + const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions. + for (int cell = 0; cell < cell_count; cell++) { ImGui::TableNextColumn(); ImGui::Text("this cell %d", cell); @@ -5686,46 +5694,89 @@ namespace ImGui { extern ImGuiKeyData* GetKeyData(ImGuiKey key); } static void ShowDemoWindowInputs() { - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus"); - if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) + IMGUI_DEMO_MARKER("Inputs & Focus"); + if (ImGui::CollapsingHeader("Inputs & Focus")) { ImGuiIO& io = ImGui::GetIO(); - // Display ImGuiIO output flags - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Output"); + // Display inputs submitted to ImGuiIO + IMGUI_DEMO_MARKER("Inputs & Focus/Inputs"); ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Output")) + if (ImGui::TreeNode("Inputs")) { + HelpMarker( + "This is a simplified view. See more detailed input state:\n" + "- in 'Tools->Metrics/Debugger->Inputs'.\n" + "- in 'Tools->Debug Log->IO'."); + if (ImGui::IsMousePosValid()) + ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); + else + ImGui::Text("Mouse pos: "); + ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); + ImGui::Text("Mouse down:"); + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); + + // 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. +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; +#else + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array +#endif + ImGui::Text("Keys down:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); ImGui::SameLine(); ImGui::Text("(%.02f)", ImGui::GetKeyData(key)->DownDuration); } + ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); + ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::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. + + ImGui::TreePop(); + } + + // Display ImGuiIO output flags + IMGUI_DEMO_MARKER("Inputs & Focus/Outputs"); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode("Outputs")) + { + HelpMarker( + "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " + "to instruct your application of how to route inputs. Typically, when a value is true, it means " + "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" + "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " + "and underlying application should ignore mouse inputs (in practice there are many and more subtle " + "rules leading to how those flags are set)."); ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse); ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("io.WantTextInput: %d", io.WantTextInput); ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos); ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible); - ImGui::TreePop(); - } - // Display Mouse state - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse State"); - if (ImGui::TreeNode("Mouse State")) - { - if (ImGui::IsMousePosValid()) - ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); - else - ImGui::Text("Mouse pos: "); - ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); + IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override"); + if (ImGui::TreeNode("WantCapture override")) + { + HelpMarker( + "Hovering the colored canvas will override io.WantCaptureXXX fields.\n" + "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering and true when clicking."); + static int capture_override_mouse = -1; + static int capture_override_keyboard = -1; + const char* capture_override_desc[] = { "None", "Set to false", "Set to true" }; + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); + ImGui::SliderInt("SetNextFrameWantCaptureMouse() on hover", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); + ImGui::SliderInt("SetNextFrameWantCaptureKeyboard() on hover", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp); - int count = IM_ARRAYSIZE(io.MouseDown); - ImGui::Text("Mouse down:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, ImGui::GetMouseClickedCount(i)); } - ImGui::Text("Mouse released:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); - ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused + ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item + if (ImGui::IsItemHovered() && capture_override_mouse != -1) + ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1); + if (ImGui::IsItemHovered() && capture_override_keyboard != -1) + ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1); + + ImGui::TreePop(); + } ImGui::TreePop(); } // Display mouse cursors - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse Cursors"); + IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors"); if (ImGui::TreeNode("Mouse Cursors")) { const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed" }; @@ -5753,113 +5804,7 @@ static void ShowDemoWindowInputs() ImGui::TreePop(); } - // Display Keyboard/Mouse state - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard, Gamepad & Navigation State"); - if (ImGui::TreeNode("Keyboard, Gamepad & Navigation State")) - { - // 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. -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; - const ImGuiKey key_first = (ImGuiKey)ImGuiKey_NamedKey_BEGIN; -#else - struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array - const ImGuiKey key_first = (ImGuiKey)0; - //ImGui::Text("Legacy raw:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } -#endif - ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } } - ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } - ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } - ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::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. - - // Draw an arbitrary US keyboard layout to visualize translated keys - { - 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 = ImGui::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. - ImGui::Dummy(ImVec2(board_max.x - board_min.x, board_max.y - board_min.y)); - if (ImGui::IsItemVisible()) - { - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - 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 = ImVec2(key_min.x + key_size.x, key_min.y + key_size.y); - 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(); - } - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Capture override")) - { - HelpMarker( - "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " - "to instruct your application of how to route inputs. Typically, when a value is true, it means " - "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" - "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " - "and underlying application should ignore mouse inputs (in practice there are many and more subtle " - "rules leading to how those flags are set)."); - - ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse); - ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); - ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard); - - HelpMarker( - "Hovering the colored canvas will override io.WantCaptureXXX fields.\n" - "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering and true when clicking."); - static int capture_override_mouse = -1; - static int capture_override_keyboard = -1; - const char* capture_override_desc[] = { "None", "Set to false", "Set to true" }; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderInt("SetNextFrameWantCaptureMouse()", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp); - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderInt("SetNextFrameWantCaptureKeyboard()", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp); - - ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(256.0f, 192.0f)); // Dummy item - if (ImGui::IsItemHovered() && capture_override_mouse != -1) - ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1); - if (ImGui::IsItemHovered() && capture_override_keyboard != -1) - ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Tabbing"); + IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing"); if (ImGui::TreeNode("Tabbing")) { ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); @@ -5875,7 +5820,7 @@ static void ShowDemoWindowInputs() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Focus from code"); + IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code"); if (ImGui::TreeNode("Focus from code")) { bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); @@ -5917,7 +5862,7 @@ static void ShowDemoWindowInputs() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Dragging"); + IMGUI_DEMO_MARKER("Inputs & Focus/Dragging"); if (ImGui::TreeNode("Dragging")) { ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); @@ -7087,7 +7032,7 @@ static void ShowExampleAppLayout(bool* p_open) { if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Close")) *p_open = false; + if (ImGui::MenuItem("Close", "Ctrl+W")) { *p_open = false; } ImGui::EndMenu(); } ImGui::EndMenuBar(); diff --git a/libs/imgui/imgui_draw.cpp b/libs/imgui/imgui_draw.cpp index 1203af8..2d423cd 100644 --- a/libs/imgui/imgui_draw.cpp +++ b/libs/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.1 +// dear imgui, v1.89.2 // (drawing and font code) /* @@ -447,11 +447,13 @@ void ImDrawList::AddDrawCmd() // Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL void ImDrawList::_PopUnusedDrawCmd() { - if (CmdBuffer.Size == 0) - return; - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount == 0 && curr_cmd->UserCallback == NULL) + while (CmdBuffer.Size > 0) + { + ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; + if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL) + return;// break; CmdBuffer.pop_back(); + } } void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) @@ -3565,17 +3567,18 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im while (y + line_height < clip_rect.y && s < text_end) { const char* line_end = (const char*)memchr(s, '\n', text_end - s); + const char* line_next = line_end ? line_end + 1 : text_end; if (word_wrap_enabled) { // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA(). // If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both. // However it is still better than nothing performing the fast-forward! - s = CalcWordWrapPositionA(scale, s, line_end, wrap_width); + s = CalcWordWrapPositionA(scale, s, line_next, wrap_width); s = CalcWordWrapNextLineStartA(s, text_end); } else { - s = line_end ? line_end + 1 : text_end; + s = line_next; } y += line_height; } diff --git a/libs/imgui/imgui_internal.h b/libs/imgui/imgui_internal.h index 816141e..e57eab7 100644 --- a/libs/imgui/imgui_internal.h +++ b/libs/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.89.1 +// dear imgui, v1.89.2 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -56,7 +56,7 @@ Index of this file: #include // INT_MIN, INT_MAX // Enable SSE intrinsics if available -#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(IMGUI_DISABLE_SSE) +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE) #define IMGUI_ENABLE_SSE #include #endif @@ -206,12 +206,16 @@ namespace ImStb #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS #define IMGUI_DEBUG_PRINTF(_FMT,...) printf(_FMT, __VA_ARGS__) #else -#define IMGUI_DEBUG_PRINTF(_FMT,...) +#define IMGUI_DEBUG_PRINTF(_FMT,...) ((void)0) #endif #endif // Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. +#ifndef IMGUI_DISABLE_DEBUG_TOOLS #define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__) +#else +#define IMGUI_DEBUG_LOG(...) ((void)0) +#endif #define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) @@ -473,6 +477,7 @@ static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } +static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry @@ -886,10 +891,9 @@ enum ImGuiSelectableFlagsPrivate_ ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem) - ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27, // Disable padding each side with ItemSpacing * 0.5f - ImGuiSelectableFlags_NoSetKeyOwner = 1 << 28, // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26, // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_NoSetKeyOwner = 1 << 27, // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) }; // Extend ImGuiTreeNodeFlags_ @@ -1046,6 +1050,7 @@ struct IMGUI_API ImGuiMenuColumns // For a given item ID, access with ImGui::GetInputTextState() struct IMGUI_API ImGuiInputTextState { + ImGuiContext* Ctx; // parent dear imgui context ImGuiID ID; // widget id owning the text state int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not. ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. @@ -1061,7 +1066,7 @@ struct IMGUI_API ImGuiInputTextState bool Edited; // edited this frame ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. - ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } + ImGuiInputTextState(ImGuiContext* ctx) { memset(this, 0, sizeof(*this)); Ctx = ctx;} void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } int GetUndoAvailCount() const { return Stb.undostate.undo_point; } @@ -1291,7 +1296,7 @@ typedef ImS16 ImGuiKeyRoutingIndex; struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; - ImU16 Mods; // Technically we'd only need 4 bits but for simplify we store ImGuiMod_ values which need 16 bits. + ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImGuiMod_Shortcut is already translated to Ctrl/Super. ImU8 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; @@ -1760,7 +1765,10 @@ struct ImGuiContext ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; + int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL float WheelingWindowReleaseTimer; + ImVec2 WheelingWindowWheelRemainder; + ImVec2 WheelingAxisAvg; // Item/widgets state and tracking information ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] @@ -2006,6 +2014,7 @@ struct ImGuiContext ImVector TempBuffer; // Temporary text buffer ImGuiContext(ImFontAtlas* shared_font_atlas) + : InputTextState(this) { Initialized = false; FontAtlasOwnedByContext = shared_font_atlas ? false : true; @@ -2026,6 +2035,7 @@ struct ImGuiContext HoveredWindowUnderMovingWindow = NULL; MovingWindow = NULL; WheelingWindow = NULL; + WheelingWindowStartFrame = -1; WheelingWindowReleaseTimer = 0.0f; DebugHookIdInfo = 0; @@ -2237,6 +2247,9 @@ struct IMGUI_API ImGuiWindow ImVec2 WindowPadding; // Window padding at the time of Begin(). float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. float WindowBorderSize; // Window border size at the time of Begin(). + float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin(). + float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y). + float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) @@ -2505,13 +2518,14 @@ struct ImGuiTableCellData // Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs, does that needs they could be moved to ImGuiTableTempData ?) struct ImGuiTableInstanceData { - float LastOuterHeight; // Outer height from last frame // FIXME: multi-instance issue (#3955) - float LastFirstRowHeight; // Height of first row from last frame // FIXME: possible multi-instance issue? + float LastOuterHeight; // Outer height from last frame + float LastFirstRowHeight; // Height of first row from last frame (FIXME: this is used as "header height" and may be reworked) + float LastFrozenHeight; // Height of frozen section from last frame - ImGuiTableInstanceData() { LastOuterHeight = LastFirstRowHeight = 0.0f; } + ImGuiTableInstanceData() { LastOuterHeight = LastFirstRowHeight = LastFrozenHeight = 0.0f; } }; -// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData +// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs, incoming RowData struct IMGUI_API ImGuiTable { ImGuiID ID; @@ -2616,6 +2630,8 @@ struct IMGUI_API ImGuiTable bool IsResetDisplayOrderRequest; bool IsUnfrozenRows; // Set when we got past the frozen row. bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() + bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame. + bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous. bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis @@ -2779,7 +2795,6 @@ namespace ImGui //#endif // Basic Accessors - inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } @@ -2860,18 +2875,21 @@ namespace ImGui // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } - inline bool IsNamedKeyOrModKey(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; } + inline bool IsNamedKeyOrModKey(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super || key == ImGuiMod_Shortcut; } inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; } inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } + inline ImGuiKeyChord ConvertShortcutMod(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; IM_ASSERT_PARANOID(key_chord & ImGuiMod_Shortcut); return (key_chord & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); } inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) { + ImGuiContext& g = *GImGui; if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl; if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift; if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt; if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper; + if (key == ImGuiMod_Shortcut) return (g.IO.ConfigMacOSXBehaviors ? ImGuiKey_ReservedForModSuper : ImGuiKey_ReservedForModCtrl); return key; } @@ -2879,7 +2897,7 @@ namespace ImGui IMGUI_API void GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size); inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); - IMGUI_API ImVec2 GetKeyVector2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down); + IMGUI_API ImVec2 GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down); IMGUI_API float GetNavTweakPressedAmount(ImGuiAxis axis); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate); @@ -3023,8 +3041,9 @@ namespace ImGui IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset); IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); - IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); + IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); + IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); IMGUI_API void TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped); @@ -3054,25 +3073,27 @@ namespace ImGui // Widgets IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); + IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); + IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col); + IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); + IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); + IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); + + // Widgets: Window Decorations IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); - IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API void Scrollbar(ImGuiAxis axis); IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); - IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir); - IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); - IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); - IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); // Widgets low-level behaviors IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); - IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); + IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API void TreePushOverrideID(ImGuiID id); IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open); @@ -3150,6 +3171,7 @@ namespace ImGui IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); + IMGUI_API void DebugRenderKeyboardPreview(ImDrawList* draw_list); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); // Obsolete functions diff --git a/libs/imgui/imgui_tables.cpp b/libs/imgui/imgui_tables.cpp index 503c098..47fc8a2 100644 --- a/libs/imgui/imgui_tables.cpp +++ b/libs/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.1 +// dear imgui, v1.89.2 // (tables and columns code) /* @@ -395,6 +395,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->OuterRect = table->InnerWindow->Rect(); table->InnerRect = table->InnerWindow->InnerRect; IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f); + + // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned) + if (instance_no == 0) + { + table->HasScrollbarYPrev = table->HasScrollbarYCurr; + table->HasScrollbarYCurr = false; + } + table->HasScrollbarYCurr |= (table->InnerWindow->ScrollMax.y > 0.0f); } else { @@ -893,7 +901,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 4] Apply final widths based on requested widths const ImRect work_rect = table->WorkRect; const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); - const float width_avail = ((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth(); + const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920) + const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed); const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; @@ -964,7 +973,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; - column->NavLayerCurrent = (ImS8)((table->FreezeRowsCount > 0 || column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); + column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen if (offset_x_frozen && table->FreezeColumnsCount == visible_n) { @@ -1124,6 +1133,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable)) TableSortSpecsBuild(table); + // [Part 14] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns) + if (table->FreezeColumnsRequest > 0) + table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x; + if (table->FreezeRowsRequest > 0) + table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight; + table_instance->LastFrozenHeight = 0.0f; + // Initial state ImGuiWindow* inner_window = table->InnerWindow; if (table->Flags & ImGuiTableFlags_NoClip) @@ -1839,17 +1855,15 @@ void ImGui::TableEndRow(ImGuiTable* table) // get the new cursor position. if (unfreeze_rows_request) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - column->NavLayerCurrent = (ImS8)((column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); - } + table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; if (unfreeze_rows_actual) { IM_ASSERT(table->IsUnfrozenRows == false); + const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); table->IsUnfrozenRows = true; + TableGetInstanceData(table, table->InstanceCurrent)->LastFrozenHeight = y0 - table->OuterRect.Min.y; // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect - float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; diff --git a/libs/imgui/imgui_widgets.cpp b/libs/imgui/imgui_widgets.cpp index 5ecb182..0a1ea67 100644 --- a/libs/imgui/imgui_widgets.cpp +++ b/libs/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.1 +// dear imgui, v1.89.2 // (widgets code) /* @@ -126,7 +126,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); // For InputTextEx() static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); +static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); //------------------------------------------------------------------------- // [SECTION] Widgets: Text, etc. @@ -544,13 +544,21 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; if (hovered) { + // Poll mouse buttons + // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId. + // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine. + int mouse_button_clicked = -1; + int mouse_button_released = -1; + for (int button = 0; button < 3; button++) + if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here. + { + if (IsMouseClicked(button, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; } + if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; } + } + + // Process initial action if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) { - // Poll buttons - int mouse_button_clicked = -1; - if ((flags & ImGuiButtonFlags_MouseButtonLeft) && IsMouseClicked(0, test_owner_id)) { mouse_button_clicked = 0; } - else if ((flags & ImGuiButtonFlags_MouseButtonRight) && IsMouseClicked(1, test_owner_id)) { mouse_button_clicked = 1; } - else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && IsMouseClicked(2, test_owner_id)) { mouse_button_clicked = 2; } if (mouse_button_clicked != -1 && g.ActiveId != id) { if (!(flags & ImGuiButtonFlags_NoSetKeyOwner)) @@ -578,10 +586,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } if (flags & ImGuiButtonFlags_PressedOnRelease) { - int mouse_button_released = -1; - if ((flags & ImGuiButtonFlags_MouseButtonLeft) && IsMouseReleased(0, test_owner_id)) { mouse_button_released = 0; } - else if ((flags & ImGuiButtonFlags_MouseButtonRight) && IsMouseReleased(1, test_owner_id)) { mouse_button_released = 1; } - else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && IsMouseReleased(2, test_owner_id)) { mouse_button_released = 2; } if (mouse_button_released != -1) { const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior @@ -1473,7 +1477,7 @@ void ImGui::Separator() } // Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. -bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) +bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -1521,7 +1525,9 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float } } - // Render + // Render at new position + if (bg_col & IM_COL32_A_MASK) + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f); const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f); @@ -3563,9 +3569,9 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** return line_count; } -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) +static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; ImFont* font = g.Font; const float line_height = g.FontSize; const float scale = line_height / font->FontSize; @@ -3614,14 +3620,14 @@ namespace ImStb static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); + const ImVec2 size = InputTextCalcTextSizeW(obj->Ctx, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); r->x0 = 0.0f; r->x1 = size.x; r->baseline_y_delta = size.y; @@ -3637,7 +3643,7 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { if (ImGui::GetIO().ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); } +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); } #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL @@ -4675,11 +4681,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ searches_result_line_no[1] = line_count; // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; + cursor_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; cursor_offset.y = searches_result_line_no[0] * g.FontSize; if (searches_result_line_no[1] >= 0) { - select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; + select_start_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; select_start_offset.y = searches_result_line_no[1] * g.FontSize; } @@ -4748,7 +4754,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else { - ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); + ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true); if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); rect.ClipWith(clip_rect); @@ -4856,6 +4862,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); DebugLocateItemOnHover(state->ID); Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, state->CurLenW, stb_state->cursor, stb_state->select_start, stb_state->select_end); + Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), true)) // Visualize undo state { @@ -6367,8 +6374,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render - if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) - hovered = true; if (hovered || selected) { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); @@ -7303,7 +7308,7 @@ struct ImGuiTabBarSection namespace ImGui { static void TabBarLayout(ImGuiTabBar* tab_bar); - static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); + static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window); static float TabBarCalcMaxTabWidth(); static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections); @@ -7696,9 +7701,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal); } -// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. -static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) +// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack. +static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window) { + IM_ASSERT(docked_window == NULL); // master branch only + IM_UNUSED(docked_window); if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) { ImGuiID id = ImHashStr(label); @@ -8005,7 +8012,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f } IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! - bool ret = TabItemEx(tab_bar, label, p_open, flags); + bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; @@ -8046,10 +8053,10 @@ bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } - return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder); + return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); } -bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags) +bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) { // Layout whole tab bar if not already done ImGuiContext& g = *GImGui; @@ -8064,7 +8071,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, return false; const ImGuiStyle& style = g.Style; - const ImGuiID id = TabBarCalcTabID(tab_bar, label); + const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window); // If the user called us with *p_open == false, we early out and don't render. // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. @@ -8114,9 +8121,16 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab->LastFrameVisible = g.FrameCount; tab->Flags = flags; - // Append name with zero-terminator - tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); - tab_bar->TabsNames.append(label, label + strlen(label) + 1); + // Append name _WITH_ the zero-terminator + if (docked_window != NULL) + { + IM_ASSERT(docked_window == NULL); // master branch only + } + else + { + tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); + tab_bar->TabsNames.append(label, label + strlen(label) + 1); + } // Update selected tab if (!is_tab_button) @@ -8279,7 +8293,7 @@ void ImGui::SetTabItemClosed(const char* label) if (is_within_manual_tab_bar) { ImGuiTabBar* tab_bar = g.CurrentTabBar; - ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); + ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL); if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) tab->WantClose = true; // Will be processed by next call to TabBarLayout() } @@ -8297,6 +8311,12 @@ ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsave return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); } +ImVec2 ImGui::TabItemCalcSize(ImGuiWindow*) +{ + IM_ASSERT(0); // This function exists to facilitate merge with 'docking' branch. + return ImVec2(0.0f, 0.0f); +} + void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) { // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. diff --git a/libs/imgui/imstb_textedit.h b/libs/imgui/imstb_textedit.h index 75a159d..a8a8231 100644 --- a/libs/imgui/imstb_textedit.h +++ b/libs/imgui/imstb_textedit.h @@ -2,6 +2,7 @@ // This is a slightly modified version of stb_textedit.h 1.14. // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) +// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000) // Grep for [DEAR IMGUI] to find the changes. // stb_textedit.h - v1.14 - public domain - Sean Barrett @@ -524,29 +525,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s int z = STB_TEXTEDIT_STRINGLEN(str); int i=0, first; - if (n == z) { - // if it's at the end, then find the last line -- simpler than trying to - // explicitly handle this case in the regular code - if (single_line) { - STB_TEXTEDIT_LAYOUTROW(&r, str, 0); - find->y = 0; - find->first_char = 0; - find->length = z; - find->height = r.ymax - r.ymin; - find->x = r.x1; - } else { - find->y = 0; - find->x = 0; - find->height = 1; - while (i < z) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - prev_start = i; - i += r.num_chars; - } - find->first_char = i; - find->length = 0; - find->prev_first = prev_start; - } + if (n == z && single_line) { + // special case if it's at the end (may not be needed?) + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; return; } @@ -557,9 +543,13 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s STB_TEXTEDIT_LAYOUTROW(&r, str, i); if (n < i + r.num_chars) break; + if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line + break; // [DEAR IMGUI] prev_start = i; i += r.num_chars; find->y += r.baseline_y_delta; + if (i == z) // [DEAR IMGUI] + break; // [DEAR IMGUI] } find->first_char = first = i; diff --git a/libs/imgui/imstb_truetype.h b/libs/imgui/imstb_truetype.h index 643d378..35c827e 100644 --- a/libs/imgui/imstb_truetype.h +++ b/libs/imgui/imstb_truetype.h @@ -2008,7 +2008,7 @@ static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int gly start = end; } } - if (fdselector == -1) stbtt__new_buf(NULL, 0); + if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422 return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); } diff --git a/src/gui.zig b/src/gui.zig index a054a58..bd5c85f 100644 --- a/src/gui.zig +++ b/src/gui.zig @@ -243,6 +243,9 @@ pub const io = struct { pub const addKeyEvent = zguiIoAddKeyEvent; extern fn zguiIoAddKeyEvent(key: Key, down: bool) void; + pub const addInputCharactersUTF8 = zguiIoAddInputCharactersUTF8; + extern fn zguiIoAddInputCharactersUTF8(utf8_chars: ?[*:0]const u8) void; + pub const setKeyEventNativeData = zguiIoSetKeyEventNativeData; extern fn zguiIoSetKeyEventNativeData(key: Key, keycode: i32, scancode: i32) void; @@ -298,7 +301,7 @@ pub const Key = enum(u32) { four, five, six, - severn, + seven, eight, nine, a, @@ -407,11 +410,11 @@ pub const Key = enum(u32) { mouse_wheel_x, mouse_wheel_y, - pub const mod_ctrl: u32 = 1 << 12; - pub const mod_shift: u32 = 1 << 13; - pub const mod_alt: u32 = 1 << 14; - pub const mod_super: u32 = 1 << 15; - pub const mod_mask_: u32 = 0xf000; + mod_ctrl = 1 << 12, + mod_shift = 1 << 13, + mod_alt = 1 << 14, + mod_super = 1 << 15, + mod_mask_ = 0xf000, }; //-------------------------------------------------------------------------------------------------- pub const WindowFlags = packed struct(u32) { @@ -558,6 +561,10 @@ pub fn setNextWindowBgAlpha(args: SetNextWindowBgAlpha) void { zguiSetNextWindowBgAlpha(args.alpha); } extern fn zguiSetNextWindowBgAlpha(alpha: f32) void; + +/// `pub fn setKeyboardFocusHere(offset: i32) void` +pub const setKeyboardFocusHere = zguiSetKeyboardFocusHere; +extern fn zguiSetKeyboardFocusHere(offset: i32) void; //-------------------------------------------------------------------------------------------------- const Begin = struct { popen: ?*bool = null, @@ -1098,6 +1105,19 @@ extern fn zguiGetTextLineHeightWithSpacing() f32; extern fn zguiGetFrameHeight() f32; extern fn zguiGetFrameHeightWithSpacing() f32; //-------------------------------------------------------------------------------------------------- +pub fn getItemRectMax() [2]f32 { + var rect: [2]f32 = undefined; + zguiGetItemRectMax(&rect); + return rect; +} +pub fn getItemRectMin() [2]f32 { + var rect: [2]f32 = undefined; + zguiGetItemRectMin(&rect); + return rect; +} +extern fn zguiGetItemRectMax(rect: *[2]f32) void; +extern fn zguiGetItemRectMin(rect: *[2]f32) void; +//-------------------------------------------------------------------------------------------------- // // ID stack/scopes // @@ -2763,14 +2783,21 @@ pub const TableBgTarget = enum(u32) { }; pub const BeginTable = struct { + column: i32, flags: TableFlags = .{}, outer_size: [2]f32 = .{ 0, 0 }, inner_width: f32 = 0, }; pub fn beginTable(name: [:0]const u8, args: BeginTable) void { - zguiBeginTable(name, args.flags, args.outer_size, args.inner_width); + zguiBeginTable(name, args.column, args.flags, &args.outer_size, args.inner_width); } -extern fn zguiBeginTable(str_id: [*:0]const u8, column: i32, flags: TableFlags, outer_size: [2]f32, inner_width: f32) void; +extern fn zguiBeginTable( + str_id: [*:0]const u8, + column: i32, + flags: TableFlags, + outer_size: *const [2]f32, + inner_width: f32, +) void; pub fn endTable() void { zguiEndTable(); @@ -2870,6 +2897,8 @@ pub const MouseButton = enum(u32) { right = 1, middle = 2, }; +/// `pub fn isMouseDoubleClicked(mouse_button: MouseButton) bool` +pub const isMouseDoubleClicked = zguiIsMouseDoubleClicked; /// `pub fn isItemClicked(mouse_button: MouseButton) bool` pub const isItemClicked = zguiIsItemClicked; /// `pub fn isItemVisible() bool` @@ -2890,6 +2919,7 @@ pub const isAnyItemHovered = zguiIsAnyItemHovered; pub const isAnyItemActive = zguiIsAnyItemActive; /// `pub fn isAnyItemFocused() bool` pub const isAnyItemFocused = zguiIsAnyItemFocused; +extern fn zguiIsMouseDoubleClicked(mouse_button: MouseButton) bool; extern fn zguiIsItemHovered(flags: HoveredFlags) bool; extern fn zguiIsItemActive() bool; extern fn zguiIsItemFocused() bool; @@ -3023,6 +3053,8 @@ pub const endTooltip = zguiEndTooltip; extern fn zguiBeginTooltip() void; extern fn zguiEndTooltip() void; +/// `pub fn beginPopupContextWindow() bool` +pub const beginPopupContextWindow = zguiBeginPopupContextWindow; pub const PopupFlags = packed struct(u32) { mouse_button_left: bool = false, mouse_button_right: bool = false, @@ -3046,6 +3078,7 @@ pub fn openPopup(str_id: [:0]const u8, flags: PopupFlags) void { pub const endPopup = zguiEndPopup; /// `pub fn closeCurrentPopup() void` pub const closeCurrentPopup = zguiCloseCurrentPopup; +extern fn zguiBeginPopupContextWindow() bool; extern fn zguiBeginPopupModal(name: [*:0]const u8, popen: ?*bool, flags: WindowFlags) bool; extern fn zguiEndPopup() void; extern fn zguiOpenPopup(str_id: [*:0]const u8, flags: PopupFlags) void; @@ -3281,9 +3314,11 @@ pub const DrawList = *opaque { anti_aliased_fill: bool = false, allow_vtx_offset: bool = false, - _padding: u28, + _padding: u28 = 0, }; + pub const setDrawListFlags = zguiDrawList_SetFlags; + extern fn zguiDrawList_SetFlags(draw_list: DrawList, flags: DrawListFlags) void; pub const getDrawListFlags = zguiDrawList_GetFlags; extern fn zguiDrawList_GetFlags(draw_list: DrawList) DrawListFlags; diff --git a/src/main.zig b/src/main.zig index 81431ac..03bfcb8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,7 +4,7 @@ // named parameters and Zig style text formatting. // //-------------------------------------------------------------------------------------------------- -pub const version = @import("std").SemanticVersion{ .major = 0, .minor = 9, .patch = 4 }; +pub const version = @import("std").SemanticVersion{ .major = 0, .minor = 9, .patch = 5 }; pub usingnamespace @import("gui.zig"); pub const plot = @import("plot.zig"); diff --git a/src/zgui.cpp b/src/zgui.cpp index e7c8481..45774f9 100644 --- a/src/zgui.cpp +++ b/src/zgui.cpp @@ -44,6 +44,10 @@ ZGUI_API void zguiSetNextWindowBgAlpha(float alpha) { ImGui::SetNextWindowBgAlpha(alpha); } +ZGUI_API void zguiSetKeyboardFocusHere(int offset) { + ImGui::SetKeyboardFocusHere(offset); +} + ZGUI_API bool zguiBegin(const char* name, bool* p_open, ImGuiWindowFlags flags) { return ImGui::Begin(name, p_open, flags); } @@ -186,6 +190,18 @@ ZGUI_API void zguiEndGroup(void) { ImGui::EndGroup(); } +ZGUI_API void zguiGetItemRectMax(float rect[2]) { + const ImVec2 r = ImGui::GetItemRectMax(); + rect[0] = r.x; + rect[1] = r.y; +} + +ZGUI_API void zguiGetItemRectMin(float rect[2]) { + const ImVec2 r = ImGui::GetItemRectMin(); + rect[0] = r.x; + rect[1] = r.y; +} + ZGUI_API void zguiGetCursorPos(float pos[2]) { const ImVec2 p = ImGui::GetCursorPos(); pos[0] = p.x; @@ -1244,6 +1260,10 @@ ZGUI_API void zguiIoAddKeyEvent(ImGuiKey key, bool down) { ImGui::GetIO().AddKeyEvent(key, down); } +ZGUI_API void zguiIoAddInputCharactersUTF8(const char* utf8_chars) { + ImGui::GetIO().AddInputCharactersUTF8(utf8_chars); +} + ZGUI_API void zguiIoSetKeyEventNativeData(ImGuiKey key, int keycode, int scancode) { ImGui::GetIO().SetKeyEventNativeData(key, keycode, scancode); } @@ -1269,6 +1289,10 @@ ZGUI_API bool zguiIsItemClicked(ImGuiMouseButton mouse_button) { return ImGui::IsItemClicked(mouse_button); } +ZGUI_API bool zguiIsMouseDoubleClicked(ImGuiMouseButton button) { + return ImGui::IsMouseDoubleClicked(button); +} + ZGUI_API bool zguiIsItemVisible(void) { return ImGui::IsItemVisible(); } @@ -1389,6 +1413,10 @@ ZGUI_API void zguiEndTooltip(void) { ImGui::EndTooltip(); } +ZGUI_API bool zguiBeginPopupContextWindow(void) { + return ImGui::BeginPopupContextWindow(); +} + ZGUI_API bool zguiBeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) { return ImGui::BeginPopupModal(name, p_open, flags); } @@ -1413,7 +1441,7 @@ ZGUI_API void zguiBeginTable( const char* str_id, int column, ImGuiTableFlags flags, - float outer_size[2], + const float outer_size[2], float inner_width ) { ImGui::BeginTable(str_id, column, flags, { outer_size[0], outer_size[1] }, inner_width); @@ -1564,6 +1592,10 @@ ZGUI_API int zguiDrawList_GetCmdBufferLength(ImDrawList *draw_list) { ZGUI_API ImDrawCmd *zguiDrawList_GetCmdBufferData(ImDrawList *draw_list) { return draw_list->CmdBuffer.begin(); } + +ZGUI_API void zguiDrawList_SetFlags(ImDrawList *draw_list, ImDrawListFlags flags) { + draw_list->Flags = flags; +} ZGUI_API ImDrawListFlags zguiDrawList_GetFlags(ImDrawList *draw_list) { return draw_list->Flags; }