From 6c754ed2cb483488cf728a0d80f0152cbcb5eeb8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Mar 2026 16:21:31 +0100 Subject: [PATCH] TempInputText, InputText: enforce making active via ImGuiInputTextFlags_MergedItem. Restore cursor as Rect is provided + comments. (#2718, #6690) --- imgui.cpp | 2 ++ imgui_widgets.cpp | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ac62bccdc..a10d0a0c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11108,6 +11108,8 @@ bool ImGui::ErrorLog(const char* msg) return g.IO.ConfigErrorRecoveryEnableAssert; } +// Display an error tooltip when same ID as HoveredId was submitted multiple times. +// See code in ItemHoverable() for an explanation of why we associate this error to HoveredId + code drawing of rectangles over individual items instances. void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() { #ifndef IMGUI_DISABLE_DEBUG_TOOLS diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b2020916c..376a53ca0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3694,27 +3694,33 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) } // Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets) +// - This must be submitted right after the item it is overlaying. +// - 'id' must be == 'window->GetID(label)'. See #2718 for a custom use of this. // FIXME: Facilitate using this in variety of other situations. -// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but -// the expected relationship between TempInputXXX functions and LastItemData is a little fishy. bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) { // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. // We clear ActiveID on the first frame to allow the InputText() taking it back. ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const bool init = (g.TempInputId != id); if (init) ClearActiveID(); - g.CurrentWindow->DC.CursorPos = bb.Min; - g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); + ImVec2 backup_pos = window->DC.CursorPos; + window->DC.CursorPos = bb.Min; + g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; // Using ImGuiInputTextFlags_MergedItem above will skip ItemAdd() so we poke here. + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem | ImGuiInputTextFlags_AutoSelectAll); if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. IM_ASSERT(g.ActiveId == id); g.TempInputId = g.ActiveId; } + if (g.ActiveId != id) + g.TempInputId = 0; + window->DC.CursorPos = backup_pos; return value_changed; } @@ -4780,9 +4786,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_wordwrap) wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize)); + const bool user_clicked = hovered && io.MouseClicked[0]; const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id); const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive - const bool user_clicked = hovered && io.MouseClicked[0]; + const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_MergedItem)); const ImGuiID scrollbar_id = (is_multiline && state != NULL) ? GetWindowScrollbarID(draw_window, ImGuiAxis_Y) : 0; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == scrollbar_id; const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == scrollbar_id; @@ -4793,7 +4800,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf); const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state. - const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_reactivate); + const bool init_make_active = (input_requested_by_user || input_requested_by_nav || input_requested_by_reactivate || user_scroll_finish); if (init_reload_from_user_buf) { int new_len = (int)ImStrlen(buf);