From ec2c805e48123dc4396266d38a1d5389781be2c0 Mon Sep 17 00:00:00 2001 From: Jack Knobel Date: Sat, 11 Jun 2022 20:52:27 +1000 Subject: [PATCH 1/8] Backends: support for unity builds for dx10/dx11/dx12 backends (#5387) --- backends/imgui_impl_dx10.cpp | 6 +++--- backends/imgui_impl_dx11.cpp | 6 +++--- backends/imgui_impl_dx12.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 8f29c67a..81df59b3 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -62,7 +62,7 @@ struct ImGui_ImplDX10_Data ImGui_ImplDX10_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } }; -struct VERTEX_CONSTANT_BUFFER +struct VERTEX_CONSTANT_BUFFER_DX10 { float mvp[4][4]; }; @@ -171,7 +171,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) void* mapped_resource; if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; - VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource; + VERTEX_CONSTANT_BUFFER_DX10* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX10*)mapped_resource; float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; float T = draw_data->DisplayPos.y; @@ -423,7 +423,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() // Create the constant buffer { D3D10_BUFFER_DESC desc; - desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); + desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX10); desc.Usage = D3D10_USAGE_DYNAMIC; desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index feb4cf43..52da01f2 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -63,7 +63,7 @@ struct ImGui_ImplDX11_Data ImGui_ImplDX11_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } }; -struct VERTEX_CONSTANT_BUFFER +struct VERTEX_CONSTANT_BUFFER_DX11 { float mvp[4][4]; }; @@ -177,7 +177,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) D3D11_MAPPED_SUBRESOURCE mapped_resource; if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; - VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; + VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData; float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; float T = draw_data->DisplayPos.y; @@ -435,7 +435,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() // Create the constant buffer { D3D11_BUFFER_DESC desc; - desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); + desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX11); desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index e6b141d4..b2de8249 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -74,7 +74,7 @@ struct ImGui_ImplDX12_Data ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; } }; -struct VERTEX_CONSTANT_BUFFER +struct VERTEX_CONSTANT_BUFFER_DX12 { float mvp[4][4]; }; @@ -93,7 +93,7 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic // Setup orthographic projection matrix into our constant buffer // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). - VERTEX_CONSTANT_BUFFER vertex_constant_buffer; + VERTEX_CONSTANT_BUFFER_DX12 vertex_constant_buffer; { float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; From 1d6e34f3f9af56e40a0386ef8635c85d85d5f914 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Jun 2022 14:46:55 +0200 Subject: [PATCH 2/8] Debug: Added ShowDebugLogWindow(). Internal: renamed old IMGUI_DEBUG_LOG() to IMGUI_DEBUG_PRINT(). Amended once. --- backends/imgui_impl_win32.cpp | 1 + docs/CHANGELOG.txt | 2 + imconfig.h | 2 +- imgui.cpp | 125 ++++++++++++++++++++++++++-------- imgui.h | 3 +- imgui_demo.cpp | 15 ++-- imgui_internal.h | 48 ++++++++----- imgui_tables.cpp | 8 +-- imgui_widgets.cpp | 2 +- 9 files changed, 149 insertions(+), 57 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 8977a1f6..987161bc 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -617,6 +617,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA io.AddInputCharacterUTF16((unsigned short)wParam); return 0; case WM_SETCURSOR: + // This is required to restore cursor when transitioning from e.g resize borders to client area. if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) return 1; return 0; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 22152e96..8ee80089 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -111,6 +111,8 @@ Other Changes: - DrawList: Fixed texture-based anti-aliasing path with RGBA textures (#5132, #3245) [@cfillion] - DrawList: Fixed divide-by-zero or glitches with Radius/Rounding values close to zero. (#5249, #5293, #3491) - DrawList: Circle with a radius smaller than 0.5f won't appear, to be consistent with other primitives. [@thedmd] +- Debug: Added ShowDebugLogWindow() showing an opt-in synthetic log of principal events (focus, popup, + active id changes) helping to diagnose issues. - Debug: Added DebugTextEncoding() function to facilitate diagnosing issues when not sure about whether you have a UTF-8 text encoding issue or a font loading issue. [@LaMarche05, @ocornut] - Demo: Add better demo of how to use SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). diff --git a/imconfig.h b/imconfig.h index 9a2aca4b..a01f803f 100644 --- a/imconfig.h +++ b/imconfig.h @@ -34,7 +34,7 @@ // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. -//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty. +//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty. //---- Don't implement some functions to reduce linkage requirements. //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) diff --git a/imgui.cpp b/imgui.cpp index bfd624f7..5af94ddc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -83,6 +83,7 @@ CODE // [SECTION] VIEWPORTS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW +// [SECTION] DEBUG LOG WINDOW // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) */ @@ -1269,7 +1270,7 @@ void ImGuiIO::ClearInputKeys() // - float analog_value: 0.0f..1.0f void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) { - //if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } + //if (e->Down) { IMGUI_DEBUG_PRINT("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } if (key == ImGuiKey_None || !AppAcceptingEvents) return; ImGuiContext& g = *GImGui; @@ -3385,6 +3386,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) { + IMGUI_DEBUG_LOG_ACTIVEID("[activeid] SetActiveID(0x%08X) in Window \"%s\"\n", id, window ? window->Name : ""); g.ActiveIdTimer = 0.0f; g.ActiveIdHasBeenPressedBefore = false; g.ActiveIdHasBeenEditedBefore = false; @@ -4576,6 +4578,7 @@ void ImGui::Shutdown() g.LogFile = NULL; } g.LogBuffer.clear(); + g.DebugLogBuf.clear(); g.Initialized = false; } @@ -5361,7 +5364,7 @@ static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; - //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); + //IMGUI_DEBUG_PRINT("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); // Create window the first time ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); @@ -6781,6 +6784,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) if (g.NavWindow != window) { + IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\")\n", window ? window->Name : ""); g.NavWindow = window; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -6790,7 +6794,6 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavLayer = ImGuiNavLayer_Main; g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); - //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } // Close popups if any @@ -7819,21 +7822,23 @@ void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse) g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0; } +#ifndef IMGUI_DISABLE_METRICS_WINDOW static const char* GetInputSourceName(ImGuiInputSource source) { const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); return input_source_names[source]; } +#endif -/*static void DebugLogInputEvent(const char* prefix, const ImGuiInputEvent* e) +/*static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) { - if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_LOG("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } - if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } - if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } - if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } + if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_PRINT("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } + if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_PRINT("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } + if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_PRINT("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } + if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_PRINT("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } + if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_PRINT("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } + if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_PRINT("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } }*/ // Process input queue @@ -7953,14 +7958,14 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) } // Record trail (for domain-specific applications wanting to access a precise trail) - //if (event_n != 0) IMGUI_DEBUG_LOG("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); + //if (event_n != 0) IMGUI_DEBUG_PRINT("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); for (int n = 0; n < event_n; n++) g.InputEventsTrail.push_back(g.InputEventsQueue[n]); // [DEBUG] /*if (event_n != 0) for (int n = 0; n < g.InputEventsQueue.Size; n++) - DebugLogInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);*/ + DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);*/ // Remaining events will be processed on the next frame if (event_n == g.InputEventsQueue.Size) @@ -9080,7 +9085,9 @@ ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; - OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags); + ImGuiID id = g.CurrentWindow->GetID(str_id); + IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X\n", str_id, id); + OpenPopupEx(id, popup_flags); } void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) @@ -9111,7 +9118,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; - IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id); + IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id); if (g.OpenPopupStack.Size < current_stack_size + 1) { g.OpenPopupStack.push_back(popup_ref); @@ -9180,7 +9187,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below { - IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); + IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : ""); ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); } } @@ -9203,7 +9210,7 @@ void ImGui::ClosePopupsExceptModals() void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; - IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); + IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); // Trim open popup stack @@ -9248,7 +9255,7 @@ void ImGui::CloseCurrentPopup() break; popup_idx--; } - IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); + IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); ClosePopupToLevel(popup_idx, true); // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. @@ -10129,7 +10136,7 @@ static void ImGui::NavUpdate() ImGuiIO& io = g.IO; io.WantSetMousePos = false; - //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); + //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); // Update Gamepad->Nav inputs mapping // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) @@ -10303,7 +10310,7 @@ static void ImGui::NavUpdate() { io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); io.WantSetMousePos = true; - //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); + //IMGUI_DEBUG_PRINT("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); } // [DEBUG] @@ -10327,7 +10334,7 @@ void ImGui::NavInitRequestApplyResult() // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result if (g.NavInitRequestFromMove) @@ -10397,7 +10404,7 @@ void ImGui::NavUpdateCreateMoveRequest() // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) if (g.NavMoveSubmitted && g.NavId == 0) { - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer); g.NavInitRequest = g.NavInitRequestFromMove = true; g.NavInitResultId = 0; g.NavDisableHighlight = false; @@ -10413,7 +10420,7 @@ void ImGui::NavUpdateCreateMoveRequest() ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); + //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f); float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX; @@ -12344,8 +12351,19 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } + // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. + if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive) + 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."); + + // Stack Tool is your best friend! + Checkbox("Show Debug Log", &cfg->ShowDebugLog); + SameLine(); + MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code."); + // Stack Tool is your best friend! - Checkbox("Show stack tool", &cfg->ShowStackTool); + Checkbox("Show Stack Tool", &cfg->ShowStackTool); SameLine(); MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); @@ -12411,12 +12429,6 @@ 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(); } @@ -13086,6 +13098,58 @@ void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int wi } } +//----------------------------------------------------------------------------- +// [SECTION] DEBUG LOG +//----------------------------------------------------------------------------- + +void ImGui::DebugLog(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + DebugLogV(fmt, args); + va_end(args); +} + +void ImGui::DebugLogV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + g.DebugLogBuf.appendf("[%05d] ", g.FrameCount); + g.DebugLogBuf.appendfv(fmt, args); +} + +void ImGui::ShowDebugLogWindow(bool* p_open) +{ + ImGuiContext& g = *GImGui; + if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) + SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver); + if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1) + { + End(); + return; + } + + AlignTextToFramePadding(); + Text("Log events:"); + SameLine(); CheckboxFlags("All", &g.DebugLogFlags, ImGuiDebugLogFlags_EventMask_); + SameLine(); CheckboxFlags("ActiveId", &g.DebugLogFlags, ImGuiDebugLogFlags_EventActiveId); + SameLine(); CheckboxFlags("Focus", &g.DebugLogFlags, ImGuiDebugLogFlags_EventFocus); + SameLine(); CheckboxFlags("Popup", &g.DebugLogFlags, ImGuiDebugLogFlags_EventPopup); + SameLine(); CheckboxFlags("Nav", &g.DebugLogFlags, ImGuiDebugLogFlags_EventNav); + + if (SmallButton("Clear")) + g.DebugLogBuf.clear(); + SameLine(); + if (SmallButton("Copy")) + SetClipboardText(g.DebugLogBuf.c_str()); + BeginChild("##log", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + TextUnformatted(g.DebugLogBuf.begin(), g.DebugLogBuf.end()); // FIXME-OPT: Could use a line index, but TextUnformatted() has a semi-decent fast path for large text. + if (GetScrollY() >= GetScrollMaxY()) + SetScrollHereY(1.0f); + EndChild(); + + End(); +} + //----------------------------------------------------------------------------- // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) //----------------------------------------------------------------------------- @@ -13312,6 +13376,9 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} void ImGui::DebugNodeViewport(ImGuiViewportP*) {} +void ImGui::DebugLog(const char*, ...) {} +void ImGui::DebugLogV(const char*, va_list) {} +void ImGui::ShowDebugLogWindow(bool*) {} void ImGui::ShowStackToolWindow(bool*) {} void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} void ImGui::UpdateDebugToolItemPicker() {} diff --git a/imgui.h b/imgui.h index 4a8a4865..ae5492bf 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ 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.88 WIP" -#define IMGUI_VERSION_NUM 18728 +#define IMGUI_VERSION_NUM 18729 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -297,6 +297,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 ShowDebugLogWindow(bool* p_open = NULL); // create Debug Log window. display a simplified log of important dear imgui events. 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) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6684936c..7df35a7d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -302,13 +302,19 @@ void ImGui::ShowDemoWindow(bool* p_open) // Dear ImGui Apps (accessible from the "Tools" menu) static bool show_app_metrics = false; + static bool show_app_debug_log = false; static bool show_app_stack_tool = false; - static bool show_app_style_editor = false; static bool show_app_about = false; + static bool show_app_style_editor = 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_metrics) + ImGui::ShowMetricsWindow(&show_app_metrics); + if (show_app_debug_log) + ImGui::ShowDebugLogWindow(&show_app_debug_log); + 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) { ImGui::Begin("Dear ImGui Style Editor", &show_app_style_editor); @@ -397,6 +403,7 @@ void ImGui::ShowDemoWindow(bool* p_open) IMGUI_DEMO_MARKER("Menu/Tools"); #ifndef IMGUI_DISABLE_METRICS_WINDOW ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); + ImGui::MenuItem("Debug Log", NULL, &show_app_debug_log); ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool); #endif ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); diff --git a/imgui_internal.h b/imgui_internal.h index 309b4bd7..f627ad7d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -147,6 +147,7 @@ struct ImGuiWindowSettings; // Storage for a window .ini settings (we ke // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) +typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() @@ -193,18 +194,17 @@ namespace ImStb // [SECTION] Macros //----------------------------------------------------------------------------- -// Debug Logging -#ifndef IMGUI_DEBUG_LOG -#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) +// Debug Printing Into TTY +#ifndef IMGUI_DEBUG_PRINT +#define IMGUI_DEBUG_PRINT(_FMT,...) printf("[%05d] " _FMT, g.FrameCount, __VA_ARGS__) #endif -// Debug Logging for selected systems. Remove the '((void)0) //' to enable. -//#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log -//#define IMGUI_DEBUG_LOG_NAV IMGUI_DEBUG_LOG // Enable log -//#define IMGUI_DEBUG_LOG_IO IMGUI_DEBUG_LOG // Enable log -#define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // Disable log -#define IMGUI_DEBUG_LOG_NAV(...) ((void)0) // Disable log -#define IMGUI_DEBUG_LOG_IO(...) ((void)0) // Disable log +// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. +#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__); +#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Static Asserts #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") @@ -1486,8 +1486,20 @@ struct ImGuiSettingsHandler // [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- +enum ImGuiDebugLogFlags_ +{ + // Event types + ImGuiDebugLogFlags_None = 0, + ImGuiDebugLogFlags_EventActiveId = 1 << 0, + ImGuiDebugLogFlags_EventFocus = 1 << 1, + ImGuiDebugLogFlags_EventPopup = 1 << 2, + ImGuiDebugLogFlags_EventNav = 1 << 3, + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav +}; + struct ImGuiMetricsConfig { + bool ShowDebugLog; bool ShowStackTool; bool ShowWindowsRects; bool ShowWindowsBeginOrder; @@ -1499,14 +1511,10 @@ struct ImGuiMetricsConfig ImGuiMetricsConfig() { - ShowStackTool = false; - ShowWindowsRects = false; - ShowWindowsBeginOrder = false; - ShowTablesRects = false; + ShowDebugLog = ShowStackTool = ShowWindowsRects = ShowWindowsBeginOrder = ShowTablesRects = false; ShowDrawCmdMesh = true; ShowDrawCmdBoundingBoxes = true; - ShowWindowsRectsType = -1; - ShowTablesRectsType = -1; + ShowWindowsRectsType = ShowTablesRectsType = -1; } }; @@ -1799,6 +1807,8 @@ struct ImGuiContext int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. // Debug Tools + ImGuiDebugLogFlags DebugLogFlags; + ImGuiTextBuffer DebugLogBuf; bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; @@ -1954,6 +1964,7 @@ struct ImGuiContext LogDepthRef = 0; LogDepthToExpand = LogDepthToExpandDefault = 2; + DebugLogFlags = ImGuiDebugLogFlags_None; DebugItemPickerActive = false; DebugItemPickerBreakId = 0; @@ -2863,12 +2874,15 @@ namespace ImGui IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); + // Debug Log + IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1); + IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1); + // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } 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); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 6757999c..db5bea1b 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -539,7 +539,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit) { const float scale_factor = new_ref_scale_unit / table->RefScale; - //IMGUI_DEBUG_LOG("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor); + //IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor); for (int n = 0; n < columns_count; n++) table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor; } @@ -2100,7 +2100,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width) if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) return; - //IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width); + //IMGUI_DEBUG_PRINT("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width); ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL; // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns. @@ -3464,7 +3464,7 @@ void ImGui::TableSettingsAddSettingsHandler() // Remove Table (currently only used by TestEngine) void ImGui::TableRemove(ImGuiTable* table) { - //IMGUI_DEBUG_LOG("TableRemove() id=0x%08X\n", table->ID); + //IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; int table_idx = g.Tables.GetIndex(table); //memset(table->RawData.Data, 0, table->RawData.size_in_bytes()); @@ -3476,7 +3476,7 @@ void ImGui::TableRemove(ImGuiTable* table) // Free up/compact internal Table buffers for when it gets unused void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) { - //IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); + //IMGUI_DEBUG_PRINT("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); table->SortSpecs.Specs = NULL; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 980172e8..d54e294c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4595,7 +4595,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); IM_ASSERT(apply_new_text_length <= buf_size); } - //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); + //IMGUI_DEBUG_PRINT("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); From 2ed9e21eba92df4f374ea028bff50f8309fdcab5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Jun 2022 17:15:27 +0200 Subject: [PATCH 3/8] Nav, Internals: wrap changes to g.NavWindow into a helper function to help track/log changes. Amend 076d8fc. Eventually we should REALLY clean up the SetNavWindow SetNavID SetFocusID FocusWindow fiasco. --- docs/TODO.txt | 2 ++ imgui.cpp | 54 +++++++++++++++++++++++++++++------------------- imgui_internal.h | 1 + 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index d2f0da50..cb7e6be7 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -113,6 +113,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - layout: (R&D) local multi-pass layout mode. - layout: (R&D) bind authored layout data (created by an off-line tool), items fetch their pos/size at submission, self-optimize data structures to stable linear access. + - tables: see https://github.com/ocornut/imgui/issues/2957#issuecomment-569726095 + - group: BeginGroup() needs a border option. (~#1496) - group: IsHovered() after EndGroup() covers whole AABB rather than the intersection of individual items. Is that desirable? - group: merge deactivation/activation within same group (fwd WasEdited flag). (#2550) diff --git a/imgui.cpp b/imgui.cpp index 5af94ddc..51f3d9ba 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6784,16 +6784,13 @@ void ImGui::FocusWindow(ImGuiWindow* window) if (g.NavWindow != window) { - IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\")\n", window ? window->Name : ""); - g.NavWindow = window; + SetNavWindow(window); if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId + g.NavLayer = ImGuiNavLayer_Main; g.NavFocusScopeId = 0; g.NavIdIsAlive = false; - g.NavLayer = ImGuiNavLayer_Main; - g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; - NavUpdateAnyRequestFlag(); } // Close popups if any @@ -7387,8 +7384,7 @@ void ImGui::SetKeyboardFocusHere(int offset) ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(offset >= -1); // -1 is allowed but not below - g.NavWindow = window; - g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; + SetNavWindow(window); ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. @@ -9561,8 +9557,22 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // [SECTION] KEYBOARD/GAMEPAD NAVIGATION //----------------------------------------------------------------------------- -// FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked. -// In our terminology those should be interchangeable. Those two functions are merely a legacy artifact, so at minimum naming should be clarified. +// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked. +// In our terminology those should be interchangeable, yet right now this is super confusing. +// Those two functions are merely a legacy artifact, so at minimum naming should be clarified. + +void ImGui::SetNavWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.NavWindow != window) + { + IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : ""); + g.NavWindow = window; + } + g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; + NavUpdateAnyRequestFlag(); +} + void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; @@ -9580,15 +9590,12 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); + if (g.NavWindow != window) + SetNavWindow(window); + // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid. // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; - if (g.NavWindow != window) - { - g.NavWindow = window; - g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; - NavUpdateAnyRequestFlag(); - } g.NavId = id; g.NavLayer = nav_layer; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; @@ -9837,11 +9844,7 @@ static void ImGui::NavProcessItem() if (g.NavId == id) { if (g.NavWindow != window) - { - g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. - g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; - NavUpdateAnyRequestFlag(); - } + SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window. g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; @@ -9990,7 +9993,12 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) { ImGuiContext& g = *GImGui; if (layer == ImGuiNavLayer_Main) + { + ImGuiWindow* prev_nav_window = g.NavWindow; g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests? + if (prev_nav_window) + IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name); + } ImGuiWindow* window = g.NavWindow; if (window->NavLastIds[layer] != 0) { @@ -10527,7 +10535,11 @@ void ImGui::NavMoveRequestApplyResult() } } - g.NavWindow = result->Window; + if (g.NavWindow != result->Window) + { + IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name); + g.NavWindow = result->Window; + } if (g.ActiveId != result->ID) ClearActiveID(); if (g.NavId != result->ID) diff --git a/imgui_internal.h b/imgui_internal.h index f627ad7d..0bee386d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2671,6 +2671,7 @@ namespace ImGui IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. + IMGUI_API void SetNavWindow(ImGuiWindow* window); IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); // Focus Scope (WIP) From dd2850083596dba1ad7628a6979fd5751b955f41 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Jun 2022 19:06:44 +0200 Subject: [PATCH 4/8] Debug: Add more log. Reworked IMGUI_DEBUG_PRINT IMGUI_DEBUG_PRINTF. Added internal IsDragDropActive() helper. DebugLog() output to TTY by default. Amend 1d6e34f. --- imgui.cpp | 45 ++++++++++++++++++++++++++++++++------------- imgui_internal.h | 17 +++++++++++++---- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 51f3d9ba..67e994c9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1270,7 +1270,7 @@ void ImGuiIO::ClearInputKeys() // - float analog_value: 0.0f..1.0f void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) { - //if (e->Down) { IMGUI_DEBUG_PRINT("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } + //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } if (key == ImGuiKey_None || !AppAcceptingEvents) return; ImGuiContext& g = *GImGui; @@ -3386,7 +3386,8 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) { - IMGUI_DEBUG_LOG_ACTIVEID("[activeid] SetActiveID(0x%08X) in Window \"%s\"\n", id, window ? window->Name : ""); + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", + g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : ""); g.ActiveIdTimer = 0.0f; g.ActiveIdHasBeenPressedBefore = false; g.ActiveIdHasBeenEditedBefore = false; @@ -4350,9 +4351,16 @@ void ImGui::NewFrame() g.HoveredIdUsingMouseWheel = false; g.HoveredIdDisabled = false; - // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) - if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) + // Clear ActiveID if the item is not alive anymore. + // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd(). + // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves. + if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId) + { + IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n"); ClearActiveID(); + } + + // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) if (g.ActiveId) g.ActiveIdTimer += g.IO.DeltaTime; g.LastActiveIdTimer += g.IO.DeltaTime; @@ -5364,7 +5372,7 @@ static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; - //IMGUI_DEBUG_PRINT("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); + //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); // Create window the first time ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); @@ -7378,11 +7386,13 @@ void ImGui::PopFocusScope() g.FocusScopeStack.pop_back(); } +// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system! void ImGui::SetKeyboardFocusHere(int offset) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(offset >= -1); // -1 is allowed but not below + IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); SetNavWindow(window); @@ -7829,12 +7839,12 @@ static const char* GetInputSourceName(ImGuiInputSource source) /*static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) { - if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_PRINT("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } - if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_PRINT("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_PRINT("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } - if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_PRINT("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_PRINT("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } - if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_PRINT("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } + if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } + if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } + if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } + if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } + if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } + if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } }*/ // Process input queue @@ -7954,7 +7964,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) } // Record trail (for domain-specific applications wanting to access a precise trail) - //if (event_n != 0) IMGUI_DEBUG_PRINT("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); + //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); for (int n = 0; n < event_n; n++) g.InputEventsTrail.push_back(g.InputEventsQueue[n]); @@ -10318,7 +10328,7 @@ static void ImGui::NavUpdate() { io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); io.WantSetMousePos = true; - //IMGUI_DEBUG_PRINT("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); + //IMGUI_DEBUG_LOG_IO("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); } // [DEBUG] @@ -11027,6 +11037,12 @@ void ImGui::NavUpdateWindowingOverlay() // [SECTION] DRAG AND DROP //----------------------------------------------------------------------------- +bool ImGui::IsDragDropActive() +{ + ImGuiContext& g = *GImGui; + return g.DragDropActive; +} + void ImGui::ClearDragDrop() { ImGuiContext& g = *GImGui; @@ -13125,8 +13141,11 @@ void ImGui::DebugLog(const char* fmt, ...) void ImGui::DebugLogV(const char* fmt, va_list args) { ImGuiContext& g = *GImGui; + const int old_size = g.DebugLogBuf.size(); g.DebugLogBuf.appendf("[%05d] ", g.FrameCount); g.DebugLogBuf.appendfv(fmt, args); + if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) + IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size); } void ImGui::ShowDebugLogWindow(bool* p_open) diff --git a/imgui_internal.h b/imgui_internal.h index 0bee386d..555022cf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -195,8 +195,13 @@ namespace ImStb //----------------------------------------------------------------------------- // Debug Printing Into TTY -#ifndef IMGUI_DEBUG_PRINT -#define IMGUI_DEBUG_PRINT(_FMT,...) printf("[%05d] " _FMT, g.FrameCount, __VA_ARGS__) +// (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename) +#ifndef IMGUI_DEBUG_PRINTF +#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS +#define IMGUI_DEBUG_PRINTF(_FMT,...) printf(_FMT, __VA_ARGS__) +#else +#define IMGUI_DEBUG_PRINTF(_FMT,...) +#endif #endif // Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. @@ -205,6 +210,7 @@ namespace ImStb #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Static Asserts #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") @@ -1494,7 +1500,9 @@ enum ImGuiDebugLogFlags_ ImGuiDebugLogFlags_EventFocus = 1 << 1, ImGuiDebugLogFlags_EventPopup = 1 << 2, ImGuiDebugLogFlags_EventNav = 1 << 3, - ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav + ImGuiDebugLogFlags_EventIO = 1 << 4, + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventIO, + ImGuiDebugLogFlags_OutputToTTY = 1 << 10 // Also send output to TTY }; struct ImGuiMetricsConfig @@ -1964,7 +1972,7 @@ struct ImGuiContext LogDepthRef = 0; LogDepthToExpand = LogDepthToExpandDefault = 2; - DebugLogFlags = ImGuiDebugLogFlags_None; + DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; DebugItemPickerActive = false; DebugItemPickerBreakId = 0; @@ -2703,6 +2711,7 @@ namespace ImGui #endif // Drag and Drop + IMGUI_API bool IsDragDropActive(); IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); From 6cac48df32bb3bac017e3e3bd565f300f413a7a9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Jun 2022 11:51:19 +0200 Subject: [PATCH 5/8] Drag, Slider: rework slightly or CTRL+Click or SetKeyboardFocusHere() will show 1 change of active id in the log (rather than a set,clear,set sequence) --- imgui_widgets.cpp | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d54e294c..1a0b5c7f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2332,26 +2332,20 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) format = PatchFormatStringFloatToInt(format); - // Tabbing or CTRL-clicking on Drag turns it into an InputText const bool hovered = ItemHoverable(frame_bb, id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { + // Tabbing or CTRL-clicking on Drag turns it into an InputText 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.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 (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) - temp_input_is_active = true; - } + const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id); + if (make_active && temp_input_allowed) + if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + temp_input_is_active = true; - // Experimental: simple click (without moving) turns Drag into an InputText + // (Optional) simple click (without moving) turns Drag into an InputText if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { @@ -2359,6 +2353,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, g.NavActivateFlags = ImGuiActivateFlags_PreferInput; temp_input_is_active = true; } + + if (make_active && !temp_input_is_active) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); + } } if (temp_input_is_active) @@ -2950,21 +2952,24 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) format = PatchFormatStringFloatToInt(format); - // Tabbing or CTRL-clicking on Slider turns it into an input box const bool hovered = ItemHoverable(frame_bb, id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { + // Tabbing or CTRL-clicking on Slider turns it into an input box const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id) + const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id); + if (make_active && temp_input_allowed) + if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id) + temp_input_is_active = true; + + if (make_active && !temp_input_is_active) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) - temp_input_is_active = true; } } From ddcff103432adf9e74c81c38e660122caa9ec0fa Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Jun 2022 14:30:20 +0200 Subject: [PATCH 6/8] Settings: Fixed some SetNextWindowPos/SetNextWindowSize API calls not marking settings as dirty. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 37 ++++++++++++++----------------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8ee80089..b2a4e27b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -107,6 +107,7 @@ Other Changes: from the menu-bar, avoiding accidental hovering from one to the other. (#3496, #4797) [@rokups] - Stack Tool: Added option to copy item path to clipboard. (#4631) - Settings: Fixed out-of-bounds read when .ini file on disk is empty. (#5351) [@quantum5] +- Settings: Fixed some SetNextWindowPos/SetNextWindowSize API calls not marking settings as dirty. - DrawList: Fixed PathArcTo() emitting terminating vertices too close to arc vertices. (#4993) [@thedmd] - DrawList: Fixed texture-based anti-aliasing path with RGBA textures (#5132, #3245) [@cfillion] - DrawList: Fixed divide-by-zero or glitches with Radius/Rounding values close to zero. (#5249, #5293, #3491) diff --git a/imgui.cpp b/imgui.cpp index 67e994c9..57a4db6e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3867,11 +3867,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; - if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) - { - MarkIniSettingsDirty(moving_window); - SetWindowPos(moving_window, pos, ImGuiCond_Always); - } + SetWindowPos(moving_window, pos, ImGuiCond_Always); FocusWindow(g.MovingWindow); } else @@ -7140,6 +7136,9 @@ void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) const ImVec2 old_pos = window->Pos; window->Pos = ImFloor(pos); ImVec2 offset = window->Pos - old_pos; + if (offset.x == 0.0f && offset.y == 0.0f) + return; + MarkIniSettingsDirty(window); window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. window->DC.IdealMaxPos += offset; @@ -7174,26 +7173,19 @@ void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); // Set - if (size.x > 0.0f) - { - window->AutoFitFramesX = 0; - window->SizeFull.x = IM_FLOOR(size.x); - } - else - { - window->AutoFitFramesX = 2; + ImVec2 old_size = window->SizeFull; + window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0; + window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0; + if (size.x <= 0.0f) window->AutoFitOnlyGrows = false; - } - if (size.y > 0.0f) - { - window->AutoFitFramesY = 0; - window->SizeFull.y = IM_FLOOR(size.y); - } else - { - window->AutoFitFramesY = 2; + window->SizeFull.x = IM_FLOOR(size.x); + if (size.y <= 0.0f) window->AutoFitOnlyGrows = false; - } + else + window->SizeFull.y = IM_FLOOR(size.y); + if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) + MarkIniSettingsDirty(window); } void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond) @@ -10930,7 +10922,6 @@ static void ImGui::NavUpdateWindowing() const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); - MarkIniSettingsDirty(moving_window); g.NavDisableMouseHover = true; } } From 27343efb0b1f8b5dac3fe6ff3511c0f7d4b92b0f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Jun 2022 14:55:45 +0200 Subject: [PATCH 7/8] Nav, Focus: Changed SetKeyboardFocusHere() to not behave if a drag or window moving is in progress + move KeepAliveID() call from Scrollbar() to ScrollbarEx() --- docs/CHANGELOG.txt | 1 + imgui.cpp | 11 +++++++++++ imgui_widgets.cpp | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b2a4e27b..06619257 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -84,6 +84,7 @@ Other Changes: - Nav: Fixed nav movement in a scope with only one disabled item from focusing the disabled item. (#5189) - Nav: Fixed issues with nav request being transferred to another window when calling SetKeyboardFocusHere() and simultaneous changing window focus. (#4449) +- Nav: Changed SetKeyboardFocusHere() to not behave if a drag or window moving is in progress. - IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the return value is overriden by focus when gamepad/keyboard navigation is active. - InputText: Fixed pressing Tab emitting two tabs characters because of dual Keys/Chars events being diff --git a/imgui.cpp b/imgui.cpp index 57a4db6e..3b5bcf2f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7386,6 +7386,17 @@ void ImGui::SetKeyboardFocusHere(int offset) IM_ASSERT(offset >= -1); // -1 is allowed but not below IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); + // It makes sense in the vast majority of cases to never interrupt a drag and drop. + // When we refactor this function into ActivateItem() we may want to make this an option. + // Note that g.ActiveId being stolen while g.MovingWindow != NULL is currently ill-defined (subtle side-effects on master, assert in docking), + // so there's another layer we need to fix. Would make sense to automatically drop g.MovingWindow when g.ActiveId is changed. + // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys() but we may need to enforce a better more encompassing scheme. + if (g.DragDropActive || g.MovingWindow != NULL) + { + IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere() ignored while DragDropActive!\n"); + return; + } + SetNavWindow(window); ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1a0b5c7f..de8afbb8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -880,9 +880,7 @@ void ImGui::Scrollbar(ImGuiAxis axis) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = GetWindowScrollbarID(window, axis); - KeepAliveID(id); // Calculate scrollbar bounding box ImRect bb = GetWindowScrollbarRect(window, axis); @@ -920,6 +918,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 if (window->SkipItems) return false; + KeepAliveID(id); + const float bb_frame_width = bb_frame.GetWidth(); const float bb_frame_height = bb_frame.GetHeight(); if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f) From 08572189f01902b5beeb84c0c28d4b1c5136b2aa Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Jun 2022 14:58:44 +0200 Subject: [PATCH 8/8] MovingWindow auto-cancelled if active id is stolen (instead of ill-defined bahavior + assert in docking). Followup to 27343ef --- imgui.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3b5bcf2f..d7ac6f63 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3383,11 +3383,21 @@ void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window) void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; + + // While most behaved code would make an effort to not steal active id during window move/drag operations, + // we at least need to be resilient to it. Cancelling the move is rather aggressive and users of 'master' branch + // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. + if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) + { + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); + g.MovingWindow = NULL; + } + + // Set active id g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", - g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : ""); + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : ""); g.ActiveIdTimer = 0.0f; g.ActiveIdHasBeenPressedBefore = false; g.ActiveIdHasBeenEditedBefore = false; @@ -7388,9 +7398,8 @@ void ImGui::SetKeyboardFocusHere(int offset) // It makes sense in the vast majority of cases to never interrupt a drag and drop. // When we refactor this function into ActivateItem() we may want to make this an option. - // Note that g.ActiveId being stolen while g.MovingWindow != NULL is currently ill-defined (subtle side-effects on master, assert in docking), - // so there's another layer we need to fix. Would make sense to automatically drop g.MovingWindow when g.ActiveId is changed. - // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys() but we may need to enforce a better more encompassing scheme. + // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but + // is also automatically dropped in the event g.ActiveId is stolen. if (g.DragDropActive || g.MovingWindow != NULL) { IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere() ignored while DragDropActive!\n");