From 7e0cccc269f6476f79172f4446ab902f6c451f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Sun, 31 Oct 2021 20:07:21 -0700 Subject: [PATCH] Updated ImGui. --- 3rdparty/dear-imgui/imgui.cpp | 685 +++++++++++++------ 3rdparty/dear-imgui/imgui.h | 23 +- 3rdparty/dear-imgui/imgui_demo.cpp | 26 +- 3rdparty/dear-imgui/imgui_draw.cpp | 2 +- 3rdparty/dear-imgui/imgui_internal.h | 93 ++- 3rdparty/dear-imgui/imgui_tables.cpp | 15 +- 3rdparty/dear-imgui/imgui_widgets.cpp | 123 ++-- 3rdparty/dear-imgui/widgets/range_slider.inl | 2 +- 8 files changed, 645 insertions(+), 324 deletions(-) diff --git a/3rdparty/dear-imgui/imgui.cpp b/3rdparty/dear-imgui/imgui.cpp index 57916f035..9929ef383 100644 --- a/3rdparty/dear-imgui/imgui.cpp +++ b/3rdparty/dear-imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.86 WIP // (main code and documentation) // Help: @@ -82,6 +82,7 @@ CODE // [SECTION] VIEWPORTS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) */ @@ -379,7 +380,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead. + - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder @@ -722,9 +723,11 @@ CODE Q&A: Usage ---------- - Q: Why is my widget not reacting when I click on it? - Q: How can I have widgets with an empty label? - Q: How can I have multiple widgets with the same label? + Q: About the ID Stack system.. + - Why is my widget not reacting when I click on it? + - How can I have widgets with an empty label? + - How can I have multiple widgets with the same label? + - How can I have multiple windows with the same label? Q: How can I display an image? What is ImTextureID, how does it works? Q: How can I use my own math types instead of ImVec2/ImVec4? Q: How can I interact with standard C++ types (such as std::string and std::vector)? @@ -916,6 +919,7 @@ static void NavUpdateCancelRequest(); static void NavUpdateCreateMoveRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); +static void NavUpdateCreateWrappingRequest(); static void NavEndFrame(); static bool NavScoreItem(ImGuiNavItemData* result); static void NavApplyItemToResult(ImGuiNavItemData* result); @@ -926,16 +930,17 @@ static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); static int FindWindowFocusIndex(ImGuiWindow* window); -// Error Checking +// Error Checking and Debug Tools static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); +static void UpdateDebugToolItemPicker(); +static void UpdateDebugToolStackQueries(); // Misc static void UpdateSettings(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateTabFocus(); -static void UpdateDebugToolItemPicker(); 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); @@ -2266,7 +2271,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items if (g.NavMoveScoringItems) unclipped_rect.Add(g.NavScoringRect); if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); // Could store and use NavJustMovedToRectRel + unclipped_rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel const ImVec2 pos = window->DC.CursorPos; int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); @@ -2969,10 +2974,9 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2981,10 +2985,9 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2993,10 +2996,9 @@ ImGuiID ImGuiWindow::GetID(int n) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -3004,10 +3006,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -3015,10 +3016,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -3026,10 +3026,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -3201,7 +3200,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function // Test if we are hovering the right window (our window could be behind another window) // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) @@ -3309,7 +3308,6 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) // Increment counters // FIXME: ImGuiItemFlags_Disabled should disable more. const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->DC.FocusCounterRegular++; if (is_tab_stop) { window->DC.FocusCounterTabStop++; @@ -3319,7 +3317,7 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. // (Note that we can always TAB out of a widget that doesn't allow tabbing in) - if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL) + if (g.ActiveId == id && g.TabFocusPressed && g.TabFocusRequestNextWindow == NULL) { g.TabFocusRequestNextWindow = window; g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. @@ -3328,11 +3326,6 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) // Handle focus requests if (g.TabFocusRequestCurrWindow == window) { - if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular) - { - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByCode; - return; - } if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) { g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor @@ -3704,25 +3697,26 @@ static void ImGui::UpdateMouseInputs() for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; + g.IO.MouseClickedCount[i] = 0; // Will be filled below g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseDoubleClicked[i] = false; if (g.IO.MouseClicked[i]) { + bool is_repeated_click = false; if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) { ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseDoubleClicked[i] = true; - g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click + is_repeated_click = true; } + if (is_repeated_click) + g.IO.MouseClickedLastCount[i]++; else - { - g.IO.MouseClickedTime[i] = g.Time; - } + g.IO.MouseClickedLastCount[i] = 1; + g.IO.MouseClickedTime[i] = g.Time; g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; + g.IO.MouseClickedCount[i] = g.IO.MouseClickedLastCount[i]; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; } @@ -3734,9 +3728,12 @@ static void ImGui::UpdateMouseInputs() g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } - if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) - g.IO.MouseDownWasDoubleClick[i] = false; - if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + + // We provide io.MouseDoubleClicked[] as a legacy service + g.IO.MouseDoubleClicked[i] = (g.IO.MouseClickedCount[i] == 2); + + // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (g.IO.MouseClicked[i]) g.NavDisableMouseHover = false; } } @@ -3841,7 +3838,10 @@ void ImGui::UpdateTabFocus() ImGuiContext& g = *GImGui; // Pressing TAB activate widget focus - g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); + g.TabFocusPressed = false; + if (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + if (!g.IO.KeyCtrl && !g.IO.KeyAlt && IsKeyPressedMap(ImGuiKey_Tab) && !IsActiveIdUsingKey(ImGuiKey_Tab)) + g.TabFocusPressed = true; if (g.ActiveId == 0 && g.TabFocusPressed) { // - This path is only taken when no widget are active/tabbed-into yet. @@ -3849,7 +3849,6 @@ void ImGui::UpdateTabFocus() // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also // manipulate the Next fields here even though they will be turned into Curr fields below. g.TabFocusRequestNextWindow = g.NavWindow; - g.TabFocusRequestNextCounterRegular = INT_MAX; if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0); else @@ -3858,17 +3857,15 @@ void ImGui::UpdateTabFocus() // Turn queued focus request into current one g.TabFocusRequestCurrWindow = NULL; - g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX; + g.TabFocusRequestCurrCounterTabStop = INT_MAX; if (g.TabFocusRequestNextWindow != NULL) { ImGuiWindow* window = g.TabFocusRequestNextWindow; g.TabFocusRequestCurrWindow = window; - if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) - g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); g.TabFocusRequestNextWindow = NULL; - g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX; + g.TabFocusRequestNextCounterTabStop = INT_MAX; } g.NavIdTabCounter = INT_MAX; @@ -4142,9 +4139,9 @@ void ImGui::NewFrame() for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); - for (int i = 0; i < g.TablesTempDataStack.Size; i++) - if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time) - TableGcCompactTransientBuffers(&g.TablesTempDataStack[i]); + for (int i = 0; i < g.TablesTempData.Size; i++) + if (g.TablesTempData[i].LastTimeActive >= 0.0f && g.TablesTempData[i].LastTimeActive < memory_compact_start_time) + TableGcCompactTransientBuffers(&g.TablesTempData[i]); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; @@ -4161,8 +4158,9 @@ void ImGui::NewFrame() g.ItemFlagsStack.push_back(ImGuiItemFlags_None); g.GroupStack.resize(0); - // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. + // [DEBUG] Update debug features UpdateDebugToolItemPicker(); + UpdateDebugToolStackQueries(); // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -4175,31 +4173,6 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. -void ImGui::UpdateDebugToolItemPicker() -{ - ImGuiContext& g = *GImGui; - g.DebugItemPickerBreakId = 0; - if (g.DebugItemPickerActive) - { - const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - SetMouseCursor(ImGuiMouseCursor_Hand); - if (IsKeyPressedMap(ImGuiKey_Escape)) - g.DebugItemPickerActive = false; - if (IsMouseClicked(0) && hovered_id) - { - g.DebugItemPickerBreakId = hovered_id; - g.DebugItemPickerActive = false; - } - SetNextWindowBgAlpha(0.60f); - BeginTooltip(); - Text("HoveredId: 0x%08X", hovered_id); - Text("Press ESC to abort picking."); - TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); - EndTooltip(); - } -} - void ImGui::Initialize(ImGuiContext* context) { ImGuiContext& g = *context; @@ -4282,7 +4255,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.ShrinkWidthBuffer.clear(); g.Tables.Clear(); - g.TablesTempDataStack.clear_destruct(); + g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); g.ClipboardHandlerData.clear(); @@ -4801,7 +4774,14 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDoubleClicked[button]; + return g.IO.MouseClickedCount[button] == 2; +} + +bool ImGui::IsMouseTripleClicked(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button] == 3; } // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. @@ -5481,7 +5461,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) + if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0) { // Manual auto-fit when double-clicking size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); @@ -6016,7 +5996,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); - if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2) window->WantCollapseToggle = true; if (window->WantCollapseToggle) { @@ -6346,7 +6326,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrentColumns = NULL; window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; + window->DC.FocusCounterTabStop = -1; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -6587,8 +6567,18 @@ void ImGui::FocusWindow(ImGuiWindow* window) void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - - const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1; + int start_idx = g.WindowsFocusOrder.Size - 1; + if (under_this_window != NULL) + { + // Aim at root window behind us, if we are in a child window that's our own root (see #4640) + int offset = -1; + while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow) + { + under_this_window = under_this_window->ParentWindow; + offset = 0; + } + start_idx = FindWindowFocusIndex(under_this_window) + offset; + } for (int i = start_idx; i >= 0; i--) { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. @@ -6732,9 +6722,14 @@ void ImGui::PopTextWrapPos() static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) { - window = window->RootWindow; - if (popup_hierarchy) - window = window->RootWindowPopupTree; + ImGuiWindow* last_window = NULL; + while (last_window != window) + { + last_window = window; + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + } return window; } @@ -6770,7 +6765,7 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function + IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; ImGuiWindow* ref_window = g.HoveredWindow; ImGuiWindow* cur_window = g.CurrentWindow; @@ -7112,12 +7107,16 @@ void ImGui::PopFocusScope() void ImGui::SetKeyboardFocusHere(int offset) { - IM_ASSERT(offset >= -1); // -1 is allowed but not below ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - g.TabFocusRequestNextWindow = window; - g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; - g.TabFocusRequestNextCounterTabStop = INT_MAX; + IM_ASSERT(offset >= -1); // -1 is allowed but not below + g.NavWindow = window; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_None, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + if (offset == -1) + NavMoveRequestResolveWithLastItem(); + else + g.NavTabbingInputableRemaining = offset + 1; } void ImGui::SetItemDefaultFocus() @@ -7126,15 +7125,17 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent) - { - g.NavInitRequest = false; - g.NavInitResultId = g.LastItemData.ID; - g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos); - NavUpdateAnyRequestFlag(); - if (!IsItemVisible()) - SetScrollHereY(); - } + if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent) + return; + + g.NavInitRequest = false; + g.NavInitResultId = g.LastItemData.ID; + g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); + NavUpdateAnyRequestFlag(); + + // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) + if (!IsItemVisible()) + ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); } void ImGui::SetStateStorage(ImGuiStorage* tree) @@ -7186,6 +7187,8 @@ void ImGui::PushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); window->IDStack.push_back(id); } @@ -7195,11 +7198,10 @@ void ImGui::PushOverrideID(ImGuiID id) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE + KeepAliveID(id); ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -8830,7 +8832,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) - window->NavRectRel[nav_layer] = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); + window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; @@ -9009,7 +9011,8 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) result->Window = window; result->ID = g.LastItemData.ID; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); + result->InFlags = g.LastItemData.InFlags; + result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) @@ -9030,7 +9033,7 @@ static void ImGui::NavProcessItem() if (candidate_for_nav_default_focus || g.NavInitResultId == 0) { g.NavInitResultId = id; - g.NavInitResultRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + g.NavInitResultRectRel = WindowRectAbsToRel(window, nav_bb); } if (candidate_for_nav_default_focus) { @@ -9043,18 +9046,30 @@ static void ImGui::NavProcessItem() // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) if (g.NavMoveScoringItems) { + if (item_flags & ImGuiItemFlags_Inputable) + g.NavTabbingInputableRemaining--; + if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) { ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - if (NavScoreItem(result)) - NavApplyItemToResult(result); - // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisible)) - NavApplyItemToResult(&g.NavMoveResultLocalVisible); + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + { + if (g.NavTabbingInputableRemaining == 0) + NavMoveRequestResolveWithLastItem(); + } + else + { + if (NavScoreItem(result)) + NavApplyItemToResult(result); + + // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisible)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible); + } } } @@ -9065,7 +9080,7 @@ static void ImGui::NavProcessItem() g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; - window->NavRectRel[window->DC.NavLayerCurrent] = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); // Store item bounding box (relative to window position) + window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) } } @@ -9080,6 +9095,10 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); + + if (move_flags & ImGuiNavMoveFlags_Tabbing) + move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; + g.NavMoveSubmitted = g.NavMoveScoringItems = true; g.NavMoveDir = move_dir; g.NavMoveDirForDebug = move_dir; @@ -9088,12 +9107,21 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM g.NavMoveScrollFlags = scroll_flags; g.NavMoveForwardToNextFrame = false; g.NavMoveKeyMods = g.IO.KeyMods; + g.NavTabbingInputableRemaining = 0; g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultOther.Clear(); NavUpdateAnyRequestFlag(); } +void ImGui::NavMoveRequestResolveWithLastItem() +{ + ImGuiContext& g = *GImGui; + g.NavMoveScoringItems = false; // Ensure request doesn't need more processing + NavApplyItemToResult(&g.NavMoveResultLocal); + NavUpdateAnyRequestFlag(); +} + void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; @@ -9207,7 +9235,8 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + ImGuiWindow* window = g.NavWindow; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) if (IsMousePosValid(&g.IO.MousePos)) @@ -9216,9 +9245,15 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() } else { - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) + ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); + if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) + { + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + rect_rel.Translate(window->Scroll - next_scroll); + } + ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImGuiViewport* viewport = GetMainViewport(); return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } @@ -9313,24 +9348,18 @@ static void ImGui::NavUpdate() // Process navigation move request if (g.NavMoveSubmitted) NavMoveRequestApplyResult(); + g.NavTabbingInputableRemaining = 0; g.NavMoveSubmitted = g.NavMoveScoringItems = false; - // Apply application mouse position movement, after we had a chance to process move request result. + // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) + bool set_mouse_pos = false; if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the navigated item position from last frame - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - { - io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); - io.WantSetMousePos = true; - //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); - } - g.NavMousePosDirty = false; - } + if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) + set_mouse_pos = true; + g.NavMousePosDirty = false; g.NavIdIsAlive = false; g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) @@ -9421,7 +9450,16 @@ static void ImGui::NavUpdate() if (!nav_keyboard_active && !nav_gamepad_active) { g.NavDisableHighlight = true; - g.NavDisableMouseHover = g.NavMousePosDirty = false; + g.NavDisableMouseHover = set_mouse_pos = false; + } + + // Update mouse position if requested + // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) + if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) + { + io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); + io.WantSetMousePos = true; + //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); } // [DEBUG] @@ -9430,7 +9468,7 @@ static void ImGui::NavUpdate() if (g.NavWindow) { ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG] if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } } #endif @@ -9523,7 +9561,7 @@ void ImGui::NavUpdateCreateMoveRequest() // (can't focus a visible object like we can with the mouse). if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) { - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); + ImRect window_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); @@ -9539,7 +9577,7 @@ void ImGui::NavUpdateCreateMoveRequest() if (window != NULL) { ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - scoring_rect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max); + scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); scoring_rect.TranslateY(scoring_rect_offset_y); scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); scoring_rect.Max.x = scoring_rect.Min.x; @@ -9564,7 +9602,9 @@ void ImGui::NavMoveRequestApplyResult() // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) if (result == NULL) { - if (g.NavId != 0) + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) { g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -9586,23 +9626,17 @@ void ImGui::NavMoveRequestApplyResult() // Scroll to keep newly navigated item fully into view. if (g.NavLayer == ImGuiNavLayer_Main) { - ImVec2 delta_scroll; if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { // FIXME: Should remove this float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; - delta_scroll.y = result->Window->Scroll.y - scroll_target; SetScrollY(result->Window, scroll_target); } else { - ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); - delta_scroll = ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); + ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); + ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); } - - // Offset our result position so mouse position can be applied immediately after in NavUpdate() - result->RectRel.TranslateX(-delta_scroll.x); - result->RectRel.TranslateY(-delta_scroll.y); } ClearActiveID(); @@ -9619,9 +9653,27 @@ void ImGui::NavMoveRequestApplyResult() IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); + // Tabbing: Activates Inputable or Focus non-Inputable + if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable)) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + } + + // Activate + if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_None; + } + // Enable nav highlight - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; + if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + { + g.NavDisableHighlight = false; + g.NavDisableMouseHover = g.NavMousePosDirty = true; + } } // Process NavCancel input (to close a popup, get back to parent, clear focus) @@ -9653,7 +9705,7 @@ static void ImGui::NavUpdateCancelRequest() IM_ASSERT(child_window->ChildId != 0); ImRect child_rect = child_window->Rect(); FocusWindow(parent_window); - SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos)); + SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); } else if (g.OpenPopupStack.Size > 0) { @@ -9726,7 +9778,7 @@ static float ImGui::NavUpdatePageUpPageDown() // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. // Preserve current horizontal position if we have any. - nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; + nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f; if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Down; @@ -9735,7 +9787,7 @@ static float ImGui::NavUpdatePageUpPageDown() } else if (end_pressed) { - nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; + nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y; if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Up; @@ -9756,63 +9808,66 @@ static void ImGui::NavEndFrame() NavUpdateWindowingOverlay(); // Perform wrap-around in menus + // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. - ImGuiWindow* window = g.NavWindow; - const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; - if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + NavUpdateCreateWrappingRequest(); +} + +static void ImGui::NavUpdateCreateWrappingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; + + bool do_forward = false; + ImRect bb_rel = window->NavRectRel[g.NavLayer]; + ImGuiDir clip_dir = g.NavMoveDir; + const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { - bool do_forward = false; - ImRect bb_rel = window->NavRectRel[g.NavLayer]; - ImGuiDir clip_dir = g.NavMoveDir; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { - bb_rel.Min.x = bb_rel.Max.x = - ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(-bb_rel.GetHeight()); - clip_dir = ImGuiDir_Up; - } - do_forward = true; - } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(+bb_rel.GetHeight()); - clip_dir = ImGuiDir_Down; - } - do_forward = true; - } - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = - ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(-bb_rel.GetWidth()); - clip_dir = ImGuiDir_Left; - } - do_forward = true; - } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(+bb_rel.GetWidth()); - clip_dir = ImGuiDir_Right; - } - do_forward = true; - } - if (do_forward) - { - window->NavRectRel[g.NavLayer] = bb_rel; - NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); + bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row + clip_dir = ImGuiDir_Up; } + do_forward = true; } + if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) + { + bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row + clip_dir = ImGuiDir_Down; + } + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) + { + bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column + clip_dir = ImGuiDir_Left; + } + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) + { + bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column + clip_dir = ImGuiDir_Right; + } + do_forward = true; + } + if (!do_forward) + return; + window->NavRectRel[g.NavLayer] = bb_rel; + NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) @@ -9820,6 +9875,7 @@ static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) ImGuiContext& g = *GImGui; IM_UNUSED(g); int order = window->FocusOrder; + IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking) IM_ASSERT(g.WindowsFocusOrder[order] == window); return order; } @@ -11228,22 +11284,23 @@ namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } void ImGui::ShowMetricsWindow(bool* p_open) { - if (!Begin("Dear ImGui Metrics/Debugger", p_open)) + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; + if (cfg->ShowStackTool) + ShowStackToolWindow(&cfg->ShowStackTool); + + if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - // Basic info Text("Dear ImGui %s", GetVersion()); Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); - Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); - Text("%d active allocations", io.MetricsActiveAllocations); + Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations); //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } Separator(); @@ -11297,11 +11354,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Tools if (TreeNode("Tools")) { - // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. - if (Button("Item Picker..")) - DebugStartItemPicker(); + // Stack Tool is your best friend! + Checkbox("Show stack tool", &cfg->ShowStackTool); SameLine(); - MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); @@ -11319,8 +11375,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } Unindent(); } - Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); - Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); Checkbox("Show tables rectangles", &cfg->ShowTablesRects); SameLine(); @@ -11367,6 +11421,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } + // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. + if (Button("Item Picker..")) + DebugStartItemPicker(); + SameLine(); + MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + TreePop(); } @@ -11380,6 +11440,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) { + Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); + Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) { ImGuiViewportP* viewport = g.Viewports[viewport_i]; @@ -12004,6 +12066,186 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la TreePop(); } +//----------------------------------------------------------------------------- +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) +//----------------------------------------------------------------------------- + +// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. +void ImGui::UpdateDebugToolItemPicker() +{ + ImGuiContext& g = *GImGui; + g.DebugItemPickerBreakId = 0; + if (!g.DebugItemPickerActive) + return; + + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + SetMouseCursor(ImGuiMouseCursor_Hand); + if (IsKeyPressedMap(ImGuiKey_Escape)) + g.DebugItemPickerActive = false; + if (IsMouseClicked(0) && hovered_id) + { + g.DebugItemPickerBreakId = hovered_id; + g.DebugItemPickerActive = false; + } + SetNextWindowBgAlpha(0.60f); + BeginTooltip(); + Text("HoveredId: 0x%08X", hovered_id); + Text("Press ESC to abort picking."); + TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + EndTooltip(); +} + +// [DEBUG] Stack Tool: update queries. Called by NewFrame() +void ImGui::UpdateDebugToolStackQueries() +{ + ImGuiContext& g = *GImGui; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Clear hook when stack tool is not visible + g.DebugHookIdInfo = 0; + if (g.FrameCount != tool->LastActiveFrame + 1) + return; + + // Update queries. The steps are: -1: query Stack, >= 0: query each stack item + // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time + const ImGuiID query_id = g.ActiveId ? g.ActiveId : g.HoveredIdPreviousFrame; + if (tool->QueryId != query_id) + { + tool->QueryId = query_id; + tool->StackLevel = -1; + tool->Results.resize(0); + } + if (query_id == 0) + return; + + // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) + int stack_level = tool->StackLevel; + if (stack_level >= 0 && stack_level < tool->Results.Size) + if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2) + tool->StackLevel++; + + // Update hook + stack_level = tool->StackLevel; + if (stack_level == -1) + g.DebugHookIdInfo = query_id; + if (stack_level >= 0 && stack_level < tool->Results.Size) + { + g.DebugHookIdInfo = tool->Results[stack_level].ID; + tool->Results[stack_level].QueryFrameCount++; + } +} + +// [DEBUG] Stack tool: hooks called by GetID() family functions +void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Step 0: stack query + // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget. + if (tool->StackLevel == -1) + { + tool->StackLevel++; + tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); + for (int n = 0; n < window->IDStack.Size + 1; n++) + tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; + return; + } + + // Step 1+: query for individual level + IM_ASSERT(tool->StackLevel >= 0); + if (tool->StackLevel != window->IDStack.Size) + return; + ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel]; + IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); + + int data_len; + switch (data_type) + { + case ImGuiDataType_S32: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id); + break; + case ImGuiDataType_String: + data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id); + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id); + break; + case ImGuiDataType_Pointer: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id); + break; + case ImGuiDataType_ID: + if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id); + break; + default: + IM_ASSERT(0); + } + info->QuerySuccess = true; +} + +// Stack Tool: Display UI +void ImGui::ShowStackToolWindow(bool* p_open) +{ + if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) + { + End(); + return; + } + + // Display hovered/active status + ImGuiContext& g = *GImGui; + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + const ImGuiID active_id = g.ActiveId; +#ifdef IMGUI_ENABLE_TEST_ENGINE + Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : ""); +#else + Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id); +#endif + SameLine(); + MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); + + // Display decorated stack + ImGuiStackTool* tool = &g.DebugStackTool; + tool->LastActiveFrame = g.FrameCount; + if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) + { + const float id_width = CalcTextSize("0xDDDDDDDD").x; + TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); + TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); + TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); + TableHeadersRow(); + for (int n = 0; n < tool->Results.Size; n++) + { + ImGuiStackLevelInfo* info = &tool->Results[n]; + TableNextColumn(); + Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); + + TableNextColumn(); + ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL; + if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) + Text("\"%s\" [window]", window->Name); + else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) + TextUnformatted(info->Desc); + else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. + { +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo() + Text("??? \"%s\"", label); + else +#endif + TextUnformatted("???"); + } + + TableNextColumn(); + Text("0x%08X", info->ID); + if (n == tool->Results.Size - 1) + TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); + } + EndTable(); + } + End(); +} + #else void ImGui::ShowMetricsWindow(bool*) {} @@ -12019,7 +12261,12 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} void ImGui::DebugNodeViewport(ImGuiViewportP*) {} -#endif +void ImGui::ShowStackToolWindow(bool*) {} +void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} +void ImGui::UpdateDebugToolItemPicker() {} +void ImGui::UpdateDebugToolStackQueries() {} + +#endif // #ifndef IMGUI_DISABLE_METRICS_WINDOW //----------------------------------------------------------------------------- diff --git a/3rdparty/dear-imgui/imgui.h b/3rdparty/dear-imgui/imgui.h index 5a34d2254..e3d2b41e3 100644 --- a/3rdparty/dear-imgui/imgui.h +++ b/3rdparty/dear-imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.86 WIP // (headers) // Help: @@ -63,8 +63,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18418 +#define IMGUI_VERSION "1.86 WIP" +#define IMGUI_VERSION_NUM 18504 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -92,7 +92,7 @@ Index of this file: #endif // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) +#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) #elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) @@ -307,6 +307,7 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. + IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. @@ -899,6 +900,7 @@ namespace ImGui IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down) IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseTripleClicked(ImGuiMouseButton button); // did mouse button triple-clicked? (note that a triple-click will also report IsMouseClicked() == true) IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? @@ -1286,8 +1288,8 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // Return true even if the item is disabled + ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; @@ -1741,7 +1743,7 @@ struct ImVector inline void pop_back() { IM_ASSERT(Size > 0); Size--; } inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } - inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; } + inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; } inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } @@ -1929,12 +1931,13 @@ struct ImGuiIO ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) ImVec2 MouseClickedPos[5]; // Position at time of clicking double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) + ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down + ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. bool MouseReleased[5]; // Mouse button went from Down to !Down bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5];//Track if button was clicked inside a dear imgui window. - bool MouseDownWasDoubleClick[5]; // Track if button down was a double-click float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point diff --git a/3rdparty/dear-imgui/imgui_demo.cpp b/3rdparty/dear-imgui/imgui_demo.cpp index febca565f..29bcf132f 100644 --- a/3rdparty/dear-imgui/imgui_demo.cpp +++ b/3rdparty/dear-imgui/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.86 WIP // (demo code) // Help: @@ -294,10 +294,12 @@ void ImGui::ShowDemoWindow(bool* p_open) // Dear ImGui Apps (accessible from the "Tools" menu) static bool show_app_metrics = false; + static bool show_app_stack_tool = false; static bool show_app_style_editor = false; static bool show_app_about = false; if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } + if (show_app_stack_tool) { ImGui::ShowStackToolWindow(&show_app_stack_tool); } if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); } if (show_app_style_editor) { @@ -382,7 +384,10 @@ void ImGui::ShowDemoWindow(bool* p_open) //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! if (ImGui::BeginMenu("Tools")) { +#ifndef IMGUI_DISABLE_METRICS_WINDOW ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); + ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool); +#endif ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); ImGui::EndMenu(); @@ -781,6 +786,7 @@ static void ShowDemoWindowWidgets() for (int i = 0; i < 6; i++) { // Disable the default "open on single-click behavior" + set Selected flag according to our selection. + // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. ImGuiTreeNodeFlags node_flags = base_flags; const bool is_selected = (selection_mask & (1 << i)) != 0; if (is_selected) @@ -789,7 +795,7 @@ static void ShowDemoWindowWidgets() { // Items 0..2 are Tree Node bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { @@ -810,7 +816,7 @@ static void ShowDemoWindowWidgets() // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { @@ -5513,10 +5519,16 @@ static void ShowDemoWindowMisc() 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 clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + + 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", i); } + ImGui::Text(" - clicked double:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text(" - clicked triple:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseTripleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text(" - clicked count:"); for (int i = 0; i < count; i++) if (io.MouseClickedCount[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseClickedCount[i]); } + //ImGui::Text(" - last count:"); for (int i = 0; i < count; i++) if (io.MouseClickedLastCount[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseClickedLastCount[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::TreePop(); diff --git a/3rdparty/dear-imgui/imgui_draw.cpp b/3rdparty/dear-imgui/imgui_draw.cpp index 0abc75f34..86bc0f2b5 100644 --- a/3rdparty/dear-imgui/imgui_draw.cpp +++ b/3rdparty/dear-imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.86 WIP // (drawing and font code) /* diff --git a/3rdparty/dear-imgui/imgui_internal.h b/3rdparty/dear-imgui/imgui_internal.h index 3f6c48c94..3373db061 100644 --- a/3rdparty/dear-imgui/imgui_internal.h +++ b/3rdparty/dear-imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.86 WIP // (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! @@ -24,7 +24,7 @@ Index of this file: // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow @@ -744,7 +744,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate item when focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. }; // Storage for LastItem data @@ -759,9 +759,7 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. - ImGuiItemStatusFlags_FocusedByCode = 1 << 8, // Set when the Focusable item just got focused from code. - ImGuiItemStatusFlags_FocusedByTabbing = 1 << 9, // Set when the Focusable item just got focused by Tabbing. - ImGuiItemStatusFlags_Focused = ImGuiItemStatusFlags_FocusedByCode | ImGuiItemStatusFlags_FocusedByTabbing + ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] @@ -1219,7 +1217,10 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary ImGuiNavMoveFlags_Forwarded = 1 << 7, - ImGuiNavMoveFlags_DebugNoResult = 1 << 8 + ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result + ImGuiNavMoveFlags_Tabbing = 1 << 9, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_Activate = 1 << 10, + ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 11 // Do not alter the visible state of keyboard vs mouse nav highlight }; enum ImGuiNavLayer @@ -1235,12 +1236,13 @@ struct ImGuiNavItemData ImGuiID ID; // Init,Move // Best candidate item ID ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags float DistBox; // Move // Best candidate box distance to current NavId float DistCenter; // Move // Best candidate center distance to current NavId float DistAxial; // Move // Best candidate axial distance to current NavId ImGuiNavItemData() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; } + void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; } }; //----------------------------------------------------------------------------- @@ -1382,11 +1384,12 @@ struct ImGuiSettingsHandler }; //----------------------------------------------------------------------------- -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- struct ImGuiMetricsConfig { + bool ShowStackTool; bool ShowWindowsRects; bool ShowWindowsBeginOrder; bool ShowTablesRects; @@ -1397,6 +1400,7 @@ struct ImGuiMetricsConfig ImGuiMetricsConfig() { + ShowStackTool = false; ShowWindowsRects = false; ShowWindowsBeginOrder = false; ShowTablesRects = false; @@ -1407,6 +1411,27 @@ struct ImGuiMetricsConfig } }; +struct ImGuiStackLevelInfo +{ + ImGuiID ID; + ImS8 QueryFrameCount; // >= 1: Query in progress + bool QuerySuccess; // Obtained result from DebugHookIdInfo() + char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) + + ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } +}; + +// State for Stack tool queries +struct ImGuiStackTool +{ + int LastActiveFrame; + int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level + ImGuiID QueryId; // ID to query details for + ImVector Results; + + ImGuiStackTool() { memset(this, 0, sizeof(*this)); } +}; + //----------------------------------------------------------------------------- // [SECTION] Generic context hooks //----------------------------------------------------------------------------- @@ -1448,7 +1473,6 @@ struct ImGuiContext bool WithinEndChild; // Set within EndChild() bool GcCompactAll; // Request full GC bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() - ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID() void* TestEngine; // Test engine user data // Windows state @@ -1468,6 +1492,7 @@ struct ImGuiContext float WheelingWindowTimer; // 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] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; @@ -1559,6 +1584,7 @@ struct ImGuiContext ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. int NavScoringDebugCount; // Metrics for debugging + int NavTabbingInputableRemaining; // >0 when counting items for tabbing ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) @@ -1574,9 +1600,7 @@ struct ImGuiContext // Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!) ImGuiWindow* TabFocusRequestCurrWindow; // ImGuiWindow* TabFocusRequestNextWindow; // - int TabFocusRequestCurrCounterRegular; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index - int TabFocusRequestNextCounterRegular; // Stored for next frame int TabFocusRequestNextCounterTabStop; // " bool TabFocusPressed; // Set in NewFrame() when user pressed Tab @@ -1605,9 +1629,9 @@ struct ImGuiContext // Table ImGuiTable* CurrentTable; - int CurrentTableStackIdx; - ImPool Tables; - ImVector TablesTempDataStack; + int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) + ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) + ImPool Tables; // Persistent table data ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; @@ -1671,8 +1695,9 @@ struct ImGuiContext // Debug Tools bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) - ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id + ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; + ImGuiStackTool DebugStackTool; // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. @@ -1697,7 +1722,6 @@ struct ImGuiContext WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; GcCompactAll = false; TestEngineHookItems = false; - TestEngineHookIdInfo = 0; TestEngine = NULL; WindowsActiveCount = 0; @@ -1708,6 +1732,7 @@ struct ImGuiContext WheelingWindow = NULL; WheelingWindowTimer = 0.0f; + DebugHookIdInfo = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdAllowOverlap = false; HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; @@ -1763,14 +1788,15 @@ struct ImGuiContext NavMoveKeyMods = ImGuiKeyModFlags_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavScoringDebugCount = 0; + NavTabbingInputableRemaining = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL; - TabFocusRequestCurrCounterRegular = TabFocusRequestCurrCounterTabStop = INT_MAX; - TabFocusRequestNextCounterRegular = TabFocusRequestNextCounterTabStop = INT_MAX; + TabFocusRequestCurrCounterTabStop = INT_MAX; + TabFocusRequestNextCounterTabStop = INT_MAX; TabFocusPressed = false; DimBgRatio = 0.0f; @@ -1789,7 +1815,7 @@ struct ImGuiContext memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); CurrentTable = NULL; - CurrentTableStackIdx = -1; + TablesTempDataStacked = 0; CurrentTabBar = NULL; TempInputId = 0; @@ -1877,7 +1903,6 @@ struct IMGUI_API ImGuiWindowTempData int CurrentTableIdx; // Current table index (into g.Tables) ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() - int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase when ImGuiItemFlags_Inputable (FIXME-NAV: Needs redesign) int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // Local parameters stacks @@ -2167,7 +2192,7 @@ struct ImGuiTableCellData }; // FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData -struct ImGuiTable +struct IMGUI_API ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; @@ -2273,14 +2298,14 @@ struct ImGuiTable bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis - IMGUI_API ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } - IMGUI_API ~ImGuiTable() { IM_FREE(RawData); } + ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } + ~ImGuiTable() { IM_FREE(RawData); } }; // Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). // - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. // - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. -struct ImGuiTableTempData +struct IMGUI_API ImGuiTableTempData { int TableIndex; // Index in g.Tables.Buf[] pool float LastTimeActive; // Last timestamp this structure was used @@ -2297,7 +2322,7 @@ struct ImGuiTableTempData float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() - IMGUI_API ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } + ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } }; // sizeof() ~ 12 @@ -2363,6 +2388,8 @@ namespace ImGui IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); + inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } + inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); @@ -2454,9 +2481,9 @@ namespace ImGui #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Currently refactoring focus/nav/tabbing system // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): - // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)' - // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' - // (New) IMGUI_VERSION_NUM >= 18411: using 'ItemAdd(..., ImGuiItemAddFlags_Inputable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP) // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem @@ -2498,6 +2525,7 @@ namespace ImGui IMGUI_API bool NavMoveRequestButNoResultYet(); IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestResolveWithLastItem(); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); @@ -2713,6 +2741,7 @@ namespace ImGui inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); + IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); @@ -2758,14 +2787,12 @@ IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table #ifdef IMGUI_ENABLE_TEST_ENGINE extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end); extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); + #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); #else #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0) #endif diff --git a/3rdparty/dear-imgui/imgui_tables.cpp b/3rdparty/dear-imgui/imgui_tables.cpp index 945e739a7..1c6118091 100644 --- a/3rdparty/dear-imgui/imgui_tables.cpp +++ b/3rdparty/dear-imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.86 WIP // (tables and columns code) /* @@ -340,10 +340,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Acquire temporary buffers const int table_idx = g.Tables.GetIndex(table); - g.CurrentTableStackIdx++; - if (g.CurrentTableStackIdx + 1 > g.TablesTempDataStack.Size) - g.TablesTempDataStack.resize(g.CurrentTableStackIdx + 1, ImGuiTableTempData()); - ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempDataStack[g.CurrentTableStackIdx]; + if (++g.TablesTempDataStacked > g.TablesTempData.Size) + g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); + ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; temp_data->TableIndex = table_idx; table->DrawSplitter = &table->TempData->DrawSplitter; table->DrawSplitter->Clear(); @@ -564,6 +563,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables) // + 1 (for table->RawData allocated below) // + 1 (for table->ColumnsNames, if names are used) +// Shared allocations per number of nested tables // + 1 (for table->Splitter._Channels) // + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) // Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details. @@ -1381,9 +1381,8 @@ void ImGui::EndTable() // Clear or restore current table, if any IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); - IM_ASSERT(g.CurrentTableStackIdx >= 0); - g.CurrentTableStackIdx--; - temp_data = g.CurrentTableStackIdx >= 0 ? &g.TablesTempDataStack[g.CurrentTableStackIdx] : NULL; + IM_ASSERT(g.TablesTempDataStacked > 0); + temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; if (g.CurrentTable) { diff --git a/3rdparty/dear-imgui/imgui_widgets.cpp b/3rdparty/dear-imgui/imgui_widgets.cpp index 3bccaaad0..ddfbb0493 100644 --- a/3rdparty/dear-imgui/imgui_widgets.cpp +++ b/3rdparty/dear-imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.86 WIP // (widgets code) /* @@ -564,7 +564,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[mouse_button_clicked])) + if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveId) @@ -641,7 +641,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((release_in || release_anywhere) && !g.DragDropActive) { // Report as pressed when releasing the mouse (this is the most common path) - bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownWasDoubleClick[mouse_button]; + bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps if (!is_double_click_release && !is_repeating_already) pressed = true; @@ -1799,7 +1799,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi bool value_changed = false; for (int i = 0; i < items_count; i++) { - PushID((void*)(intptr_t)i); + PushID(i); const bool item_selected = (i == *current_item); const char* item_text; if (!items_getter(data, i, &item_text)) @@ -2407,17 +2407,17 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); - if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) + const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2); + if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); if (temp_input_allowed) - if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) temp_input_is_active = true; } @@ -3040,15 +3040,15 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (focus_requested || clicked || g.NavActivateId == id || g.NavActivateInputId == id) + if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) + if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) temp_input_is_active = true; } } @@ -3694,17 +3694,18 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob } // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } +static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } 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; } -#ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(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; } -#else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(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; } -#endif +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; } #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 +#ifdef __APPLE__ // FIXME: Move setting to IO structure +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC +#else +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN +#endif static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { @@ -3896,11 +3897,12 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Generic named filters if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))) { - // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf. + // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. // Change the default decimal_point with: // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; + // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. ImGuiContext& g = *GImGui; const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; @@ -4039,21 +4041,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); - const bool focus_requested_by_code = (item_status_flags & ImGuiItemStatusFlags_FocusedByCode) != 0; - const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && (g.NavActivateInputId == id || g.NavActivateId == id); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); - bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + bool select_all = false; float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (user_clicked || user_scroll_finish || user_nav_input_start || focus_requested_by_code || focus_requested_by_tabbing); + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing); const bool init_state = (init_make_active || user_scroll_active); if ((init_state && g.ActiveId != id) || init_changed_specs) { @@ -4089,13 +4089,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->ID = id; state->ScrollX = 0.0f; stb_textedit_initialize_state(&state->Stb, !is_multiline); - if (!is_multiline && focus_requested_by_code) + } + + if (!is_multiline) + { + if (flags & ImGuiInputTextFlags_AutoSelectAll) + select_all = true; + if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) + select_all = true; + if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl)) select_all = true; } + if (flags & ImGuiInputTextFlags_AlwaysOverwrite) state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) - if (!is_multiline && (focus_requested_by_tabbing || (user_clicked && io.KeyCtrl))) - select_all = true; } if (g.ActiveId != id && init_make_active) @@ -4128,7 +4135,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Lock the decision of whether we are going to take the path displaying the cursor or selection const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); - bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; bool enter_pressed = false; @@ -4186,19 +4193,49 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) + if (select_all) { state->SelectAll(); state->SelectedAllMouseLock = true; } - else if (hovered && is_osx && io.MouseDoubleClicked[0]) + else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) { - // Double-click select a word only, OS X style (by simulating keystrokes) - state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + const int multiclick_count = (io.MouseClickedCount[0] - 2); + if ((multiclick_count % 2) == 0) + { + // Double-click: Select word + // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: + // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) + const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n'; + if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + if (!STB_TEXT_HAS_SELECTION(&state->Stb)) + ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb); + state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor); + state->Stb.select_end = state->Stb.cursor; + ImStb::stb_textedit_clamp(state, &state->Stb); + } + else + { + // Triple-click: Select line + const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n'; + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); + state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); + if (!is_eol && is_multiline) + { + ImSwap(state->Stb.select_start, state->Stb.select_end); + state->Stb.cursor = state->Stb.select_end; + } + state->CursorFollow = false; + } + state->CursorAnimReset(); } else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { + // FIXME: unselect on late click could be done release? if (hovered) { stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); @@ -4229,7 +4266,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. if (io.InputQueueCharacters.Size > 0) { - if (!ignore_char_inputs && !is_readonly && !user_nav_input_start) + if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering @@ -5929,7 +5966,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job - if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseDoubleClicked[0]) + if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) toggled = true; } else if (pressed && g.DragDropHoldJustPressedId == id) @@ -6032,7 +6069,7 @@ void ImGui::TreePushOverrideID(ImGuiID id) ImGuiWindow* window = g.CurrentWindow; Indent(); window->DC.TreeDepth++; - window->IDStack.push_back(id); + PushOverrideID(id); } void ImGui::TreePop() @@ -6236,7 +6273,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); // (bb == NavRect) + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect) g.NavDisableHighlight = true; } } @@ -7889,9 +7926,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if (p_open && !*p_open) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); return false; } @@ -7958,9 +7993,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. if (tab_appearing && (!tab_bar_appearing || tab_is_new)) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); if (is_tab_button) return false; return tab_contents_visible; diff --git a/3rdparty/dear-imgui/widgets/range_slider.inl b/3rdparty/dear-imgui/widgets/range_slider.inl index 3cec7ace6..419ec7638 100644 --- a/3rdparty/dear-imgui/widgets/range_slider.inl +++ b/3rdparty/dear-imgui/widgets/range_slider.inl @@ -183,7 +183,7 @@ bool RangeSliderFloat(const char* label, float* v1, float* v2, float v_min, floa // Tabbing or CTRL-clicking on Slider turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0; + const bool tab_focus_requested = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedByTabbing) != 0; if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) { SetActiveID(id, window);