From 0cba4f7ce8e3baca3d101fafd12c74a1cd821cd6 Mon Sep 17 00:00:00 2001 From: 4DBug <4DBug@github.com> Date: Thu, 8 Oct 2026 08:40:30 -0500 Subject: [PATCH] tools: full Metrics/Debugger window (windows, drawlists, viewports, fonts, popups, tab bars, tables, docking, settings, inputs, internal state, rect overlays) --- build/ImGui.linemap.json | 2 +- build/ImGui.luau | 795 +++++++++++++++++++++++++++++++-- imgui/imgui.lua | 49 +- imgui/imgui_demo_tools.lua | 745 ++++++++++++++++++++++++++++++ imgui/imgui_h.lua | 1 + tools/test/scripts/t_apps.luau | 1 + 6 files changed, 1496 insertions(+), 97 deletions(-) diff --git a/build/ImGui.linemap.json b/build/ImGui.linemap.json index 58887b0..1c559ba 100644 --- a/build/ImGui.linemap.json +++ b/build/ImGui.linemap.json @@ -1 +1 @@ -[[12, "imgui.lua"], [13781, "imgui_demo.lua"], [14714, "imgui_demo_2.lua"], [17789, "imgui_demo_apps.lua"], [19048, "imgui_demo_tools.lua"], [19629, "imgui_demo_widgets.lua"], [21553, "imgui_docking.lua"], [24681, "imgui_dragdrop.lua"], [24973, "imgui_draw.lua"], [30161, "imgui_h.lua"], [32373, "imgui_impl_roblox.lua"], [33160, "imgui_impl_roblox_fonts.lua"], [33938, "imgui_internal.lua"], [37108, "imgui_tables.lua"], [41455, "imgui_tabs.lua"], [42622, "imgui_widgets.lua"], [51146, "imstb_rectpack.lua"], [51592, "imstb_textedit.lua"], [52833, "imstb_truetype.lua"], [55301, "imstd_minstdio.lua"]] \ No newline at end of file +[[12, "imgui.lua"], [13734, "imgui_demo.lua"], [14667, "imgui_demo_2.lua"], [17742, "imgui_demo_apps.lua"], [19001, "imgui_demo_tools.lua"], [20327, "imgui_demo_widgets.lua"], [22251, "imgui_docking.lua"], [25379, "imgui_dragdrop.lua"], [25671, "imgui_draw.lua"], [30859, "imgui_h.lua"], [33072, "imgui_impl_roblox.lua"], [33859, "imgui_impl_roblox_fonts.lua"], [34637, "imgui_internal.lua"], [37807, "imgui_tables.lua"], [42154, "imgui_tabs.lua"], [43321, "imgui_widgets.lua"], [51845, "imstb_rectpack.lua"], [52291, "imstb_textedit.lua"], [53532, "imstb_truetype.lua"], [56000, "imstd_minstdio.lua"]] \ No newline at end of file diff --git a/build/ImGui.luau b/build/ImGui.luau index 713d9d9..cbaefbd 100644 --- a/build/ImGui.luau +++ b/build/ImGui.luau @@ -12680,7 +12680,7 @@ function ImGui.DebugRenderViewportThumbnail(draw_list, viewport, bb) end end -local function RenderViewportsThumbnails() +function ImGui.RenderViewportsThumbnails() local g = GImGui local window = g.CurrentWindow @@ -12705,53 +12705,6 @@ local function RenderViewportsThumbnails() ImGui.Dummy(bb_full:GetSize() * SCALE) end --- TODO: ---- @param open bool? -function ImGui.ShowMetricsWindow(open) - local g = GImGui - - local ret - open, ret = ImGui.Begin("ImGui Sincerely Metrics/Debugger", open) - if not ret or ImGui.GetCurrentWindow().BeginCount > 1 then - ImGui.End() - return open - end - - if ImGui.TreeNode(string.format("Viewports (%d)", g.Viewports.Size)) then - -- ImGui.SetNextItemOpen(true, ImGuiCond.Once) - if ImGui.TreeNode("Windows Minimap") then - RenderViewportsThumbnails() - ImGui.TreePop() - end - - ImGui.TreePop() - end - - if ImGui.TreeNode("Memory allocations") then - local info = g.DebugAllocInfo - ImGui.Text("%d current allocations", info.TotalAllocCount - info.TotalFreeCount) - ImGui.Text("Releasing selected unused buffers after: %.2f secs", g.IO.ConfigMemoryCompactTimer) - if ImGui.SmallButton("GC now") then - g.GcCompactAll = true - end - ImGui.Text("Recent frames with allocations:") - local buf_size = #info.LastEntriesBuf - for n = buf_size, 1, -1 do - local idx = (info.LastEntriesIdx - n + buf_size) % buf_size + 1 - local entry = info.LastEntriesBuf[idx] - ImGui.BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry.FrameCount, entry.AllocCount - entry.FreeCount, entry.AllocCount, entry.FreeCount) - if n == 0 then - ImGui.SameLine() - ImGui.Text("<- %d frames ago", g.FrameCount - entry.FrameCount) - end - end - - ImGui.TreePop() - end - - ImGui.End() - return open -end @@ -19622,6 +19575,751 @@ function ImGui.ShowIDStackToolWindow(p_open) return p_open end +---------------------------------------------------------------- +-- [SECTION] Metrics/Debugger window +---------------------------------------------------------------- + +local function B(v) return v and 1 or 0 end +local function FG(window) return ImGui.GetForegroundDrawList(window and window.Viewport or ImGui.GetMainViewport()) end +local function YellowRect(window, min, max) FG(window):AddRect(min, max, IM_COL32(255, 255, 0, 255)) end + +function ImGui.DebugNodeColumns(columns) + if not ImGui.TreeNode(columns.ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns.ID, columns.Count, columns.Flags) then return end + ImGui.BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns.OffMaxX - columns.OffMinX, columns.OffMinX, columns.OffMaxX) + for i, column in columns.Columns:iter() do + ImGui.BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", i - 1, column.OffsetNorm, ImGui.GetColumnOffsetFromNorm(columns, column.OffsetNorm)) + end + ImGui.TreePop() +end + +function ImGui.DebugNodeStorage(storage, label) + local n = 0 + for _ in pairs(storage) do n = n + 1 end + if not ImGui.TreeNode(label, "%s: %d entries", label, n) then return end + for k, v in pairs(storage) do + ImGui.BulletText("Key 0x%08X Value { %s }", tonumber(k) or 0, tostring(v)) + end + ImGui.TreePop() +end + +function ImGui.DebugNodeDrawList(window, viewport, draw_list, label) + local g = ImGui.GetCurrentContext() + local cfg = g.DebugMetricsConfig + local cmd_count = draw_list.CmdBuffer.Size + if cmd_count > 0 and draw_list.CmdBuffer.Data[cmd_count].ElemCount == 0 and draw_list.CmdBuffer.Data[cmd_count].UserCallback == nil then + cmd_count = cmd_count - 1 + end + local node_open = ImGui.TreeNode(tostring(draw_list), "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list._OwnerName or "", draw_list.VtxBuffer.Size, draw_list.IdxBuffer.Size, cmd_count) + if draw_list == ImGui.GetWindowDrawList() then + ImGui.SameLine() + ImGui.TextColored(ImVec4(1.0, 0.4, 0.4, 1.0), "CURRENTLY APPENDING") + if node_open then ImGui.TreePop() end + return + end + local fg_draw_list = viewport and ImGui.GetForegroundDrawList(viewport) or nil + if window and ImGui.IsItemHovered() and fg_draw_list then + fg_draw_list:AddRect(window.Pos, window.Pos + window.Size, IM_COL32(255, 255, 0, 255)) + end + if not node_open then return end + if window and not window.WasActive then ImGui.TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!") end + + local vtx, idx = draw_list.VtxBuffer.Data, draw_list.IdxBuffer.Data + for cmd_n = 1, cmd_count do + local pcmd = draw_list.CmdBuffer.Data[cmd_n] + if pcmd.UserCallback then + ImGui.BulletText("Callback %s", tostring(pcmd.UserCallback)) + else + local cr = pcmd.ClipRect + local open = ImGui.TreeNode(cmd_n, "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", math.floor(pcmd.ElemCount / 3), tostring(pcmd.TexRef and (pcmd.TexRef._TexID or pcmd.TexRef._TexData)), cr.x, cr.y, cr.z, cr.w) + local function ShowMesh(show_mesh, show_aabb) + if not fg_draw_list then return end + local clip_rect = ImRect(cr.x, cr.y, cr.z, cr.w) + local vtxs_rect = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX) + local base = pcmd.IdxOffset + for i = base, base + pcmd.ElemCount - 1, 3 do + local tri = {} + for n = 0, 2 do + local v = vtx[idx[i + n + 1] + pcmd.VtxOffset + 1] + if v == nil then return end + tri[n + 1] = ImVec2(v.pos.x, v.pos.y) + vtxs_rect:Add(tri[n + 1]) + end + if show_mesh then fg_draw_list:AddPolyline(tri, 3, IM_COL32(255, 255, 0, 255), 1.0, ImDrawFlags.Closed) end + end + if show_aabb then + fg_draw_list:AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)) + fg_draw_list:AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)) + end + end + if ImGui.IsItemHovered() and (cfg.ShowDrawCmdMesh or cfg.ShowDrawCmdBoundingBoxes) then ShowMesh(cfg.ShowDrawCmdMesh, cfg.ShowDrawCmdBoundingBoxes) end + if open then + ImGui.Selectable(string.format("Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d", pcmd.ElemCount, pcmd.VtxOffset, pcmd.IdxOffset)) + if ImGui.IsItemHovered() then ShowMesh(true, false) end + ImGui.TreePop() + end + end + end + ImGui.TreePop() +end + +function ImGui.DebugNodeTabBar(tab_bar, label) + local g = ImGui.GetCurrentContext() + local is_active = (tab_bar.PrevFrameVisible >= ImGui.GetFrameCount() - 2) + local names = {} + for tab_n = 1, math.min(tab_bar.Tabs.Size, 3) do + local tab = tab_bar.Tabs.Data[tab_n] + names[#names + 1] = "'" .. (ImGui.TabBarGetTabName and ImGui.TabBarGetTabName(tab_bar, tab) or "?") .. "'" + end + local buf = string.format("%s 0x%08X (%d tabs)%s { %s%s", label, tab_bar.ID, tab_bar.Tabs.Size, is_active and "" or " *Inactive*", table.concat(names, ", "), (tab_bar.Tabs.Size > 3) and " ... }" or " } ") + if not is_active then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local open = ImGui.TreeNode(label, "%s", buf) + if not is_active then ImGui.PopStyleColor() end + if is_active and ImGui.IsItemHovered() then + local dl = FG(tab_bar.Window) + dl:AddRect(tab_bar.BarRect.Min, tab_bar.BarRect.Max, IM_COL32(255, 255, 0, 255)) + dl:AddLine(ImVec2(tab_bar.ScrollingRectMinX, tab_bar.BarRect.Min.y), ImVec2(tab_bar.ScrollingRectMinX, tab_bar.BarRect.Max.y), IM_COL32(0, 255, 0, 255)) + dl:AddLine(ImVec2(tab_bar.ScrollingRectMaxX, tab_bar.BarRect.Min.y), ImVec2(tab_bar.ScrollingRectMaxX, tab_bar.BarRect.Max.y), IM_COL32(0, 255, 0, 255)) + end + if open then + for tab_n, tab in tab_bar.Tabs:iter() do + ImGui.PushID(tab_n) + if ImGui.SmallButton("<") and ImGui.TabBarQueueReorder then ImGui.TabBarQueueReorder(tab_bar, tab, -1) end ImGui.SameLine(0, 2) + if ImGui.SmallButton(">") and ImGui.TabBarQueueReorder then ImGui.TabBarQueueReorder(tab_bar, tab, 1) end ImGui.SameLine() + ImGui.Text("%02d%s Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f", tab_n - 1, (tab.ID == tab_bar.SelectedTabId) and "*" or " ", tab.ID, + ImGui.TabBarGetTabName and ImGui.TabBarGetTabName(tab_bar, tab) or "?", tab.Offset, tab.Width, tab.ContentWidth) + ImGui.PopID() + end + ImGui.TreePop() + end +end + +local function SizingDesc(f) + local TF = ImGuiTableFlags + f = bit32.band(f, TF.SizingMask_) + if f == TF.SizingFixedFit then return "FixedFit" elseif f == TF.SizingFixedSame then return "FixedSame" + elseif f == TF.SizingStretchProp then return "StretchProp" elseif f == TF.SizingStretchSame then return "StretchSame" end + return "N/A" +end + +function ImGui.DebugNodeTableSettings(settings, tbl) + local open = ImGui.TreeNode(tostring(settings), "Settings 0x%08X (%d columns)", settings.ID, settings.ColumnsCount) + local hovered = ImGui.IsItemHovered() + if hovered and tbl == nil and settings.ID ~= 0 then tbl = ImGui.TableFindByID(settings.ID) end + if hovered and tbl ~= nil then YellowRect(tbl.OuterWindow, tbl.OuterRect.Min, tbl.OuterRect.Max) end + if open then + ImGui.BulletText("SaveFlags: 0x%08X", settings.SaveFlags) + ImGui.BulletText("ColumnsCount: %d (max %d)", settings.ColumnsCount, settings.ColumnsCountMax) + for n = 0, settings.ColumnsCount - 1 do + local c = settings.Columns[n] + local dir = (c.SortOrder ~= -1) and c.SortDirection or ImGuiSortDirection.None + ImGui.BulletText("Column %d Order %d SortOrder %2d %s Vis %d %s %7.3f ID 0x%08X", n, c.DisplayOrder, c.SortOrder, + (dir == ImGuiSortDirection.Ascending) and "Asc" or ((dir == ImGuiSortDirection.Descending) and "Des" or "---"), c.IsEnabled, (c.IsStretch ~= 0) and "Weight" or "Width ", c.WidthOrWeight, c.ID) + end + ImGui.TreePop() + end +end + +function ImGui.DebugNodeTable(tbl) + local g = ImGui.GetCurrentContext() + local is_active = (tbl.LastFrameActive >= g.FrameCount - 2) + if not is_active then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local open = ImGui.TreeNode(tostring(tbl), "Table 0x%08X (%d columns, in '%s')%s", tbl.ID, tbl.ColumnsCount, tbl.OuterWindow and tbl.OuterWindow.Name or "?", is_active and "" or " *Inactive*") + if not is_active then ImGui.PopStyleColor() end + if ImGui.IsItemHovered() then YellowRect(tbl.OuterWindow, tbl.OuterRect.Min, tbl.OuterRect.Max) end + if not open then return end + if tbl.InstanceCurrent > 0 then ImGui.Text("** %d instances of same table! Some data below will refer to last instance.", tbl.InstanceCurrent + 1) end + local clear_settings = ImGui.SmallButton("Clear settings") + ImGui.BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", tbl.OuterRect.Min.x, tbl.OuterRect.Min.y, tbl.OuterRect:GetWidth(), tbl.OuterRect:GetHeight(), SizingDesc(tbl.Flags)) + ImGui.BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", tbl.ColumnsGivenWidth, tbl.ColumnsAutoFitWidth, tbl.InnerWidth, (tbl.InnerWidth == 0.0) and " (auto)" or "") + ImGui.BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", tbl.CellPaddingX, tbl.CellSpacingX1, tbl.CellSpacingX2, tbl.OuterPaddingX) + ImGui.BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", tbl.HoveredColumnBody, tbl.HoveredColumnBorder) + ImGui.BulletText("ResizedColumn: %d, HeldHeaderColumn: %d, ReorderColumn: %d", tbl.LastResizedColumn, tbl.LastHeldHeaderColumn, tbl.ReorderColumn) + for n = 0, tbl.InstanceCurrent do + local inst = ImGui.TableGetInstanceData(tbl, n) + ImGui.BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, inst.HoveredRowLast, inst.LastOuterHeight) + end + local sum_weights = 0.0 + for n = 0, tbl.ColumnsCount - 1 do + if bit32.band(tbl.Columns[n].Flags, ImGuiTableColumnFlags.WidthStretch) ~= 0 then sum_weights = sum_weights + tbl.Columns[n].StretchWeight end + end + for n = 0, tbl.ColumnsCount - 1 do + local c = tbl.Columns[n] + local CF = ImGuiTableColumnFlags + local buf = string.format( + "Column %d order %d '%s': offset %+.2f to %+.2f%s\nEnabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" .. + "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\nMinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" .. + "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\nSort: %d%s, UserData: 0x%08X, Flags: 0x%04X: %s%s%s..", + n, c.DisplayOrder, ImGui.TableGetColumnName(tbl, n) or "", c.MinX - tbl.WorkRect.Min.x, c.MaxX - tbl.WorkRect.Min.x, (n < tbl.FreezeColumnsRequest) and " (Frozen)" or "", + B(c.IsEnabled), B(c.IsVisibleX), B(c.IsVisibleY), B(c.IsRequestOutput), B(c.IsSkipItems), c.DrawChannelFrozen, c.DrawChannelUnfrozen, + c.WidthGiven, c.WidthRequest, c.WidthAuto, c.StretchWeight, (c.StretchWeight > 0.0 and sum_weights > 0) and (c.StretchWeight / sum_weights) * 100.0 or 0.0, + c.MinX, c.MaxX, c.MaxX - c.MinX, c.ClipRect.Min.x, c.ClipRect.Max.x, c.ClipRect.Max.x - c.ClipRect.Min.x, + c.ContentMaxXFrozen - c.WorkMinX, c.ContentMaxXUnfrozen - c.WorkMinX, c.ContentMaxXHeadersUsed - c.WorkMinX, c.ContentMaxXHeadersIdeal - c.WorkMinX, + c.SortOrder, (c.SortDirection == ImGuiSortDirection.Ascending) and " (Asc)" or ((c.SortDirection == ImGuiSortDirection.Descending) and " (Des)" or ""), tonumber(c.UserData) or 0, c.Flags, + (bit32.band(c.Flags, CF.WidthStretch) ~= 0) and "WidthStretch " or "", (bit32.band(c.Flags, CF.WidthFixed) ~= 0) and "WidthFixed " or "", (bit32.band(c.Flags, CF.NoResize) ~= 0) and "NoResize " or "") + ImGui.Bullet() + ImGui.Selectable(buf) + if ImGui.IsItemHovered() then YellowRect(tbl.OuterWindow, ImVec2(c.MinX, tbl.OuterRect.Min.y), ImVec2(c.MaxX, tbl.OuterRect.Max.y)) end + end + local settings = ImGui.TableGetBoundSettings(tbl) + if settings then ImGui.DebugNodeTableSettings(settings, tbl) end + if clear_settings then tbl.IsResetAllRequest = true end + ImGui.TreePop() +end + +local function DebugNodeDockNodeFlags(flags, label, enabled) + local D = ImGuiDockNodeFlags + ImGui.PushID(label) + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ImVec2(0.0, 0.0)) + ImGui.Text("%s:", label) + if not enabled then ImGui.BeginDisabled() end + for _, n in ipairs({ "NoResize", "NoResizeX", "NoResizeY", "NoTabBar", "HiddenTabBar", "NoWindowMenuButton", "NoCloseButton", "DockedWindowsInFocusRoute", + "NoDocking", "NoDockingSplit", "NoDockingSplitOther", "NoDockingOverMe", "NoDockingOverOther", "NoDockingOverEmpty", "NoUndocking" }) do + if D[n] then _, flags = ImGui.CheckboxFlags(n, flags, D[n]) end + end + if not enabled then ImGui.EndDisabled() end + ImGui.PopStyleVar() + ImGui.PopID() + return flags +end + +function ImGui.DebugNodeDockNode(node, label) + local g = ImGui.GetCurrentContext() + local is_alive = (g.FrameCount - node.LastFrameAlive < 2) + local is_active = (g.FrameCount - node.LastFrameActive < 2) + if not is_alive then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local tree_node_flags = node.IsFocused and ImGuiTreeNodeFlags.Selected or ImGuiTreeNodeFlags.None + local open + if node.Windows.Size > 0 then + open = ImGui.TreeNodeEx(node.ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node.ID, node.IsVisible and "" or " (hidden)", node.Windows.Size, node.VisibleWindow and node.VisibleWindow.Name or "NULL") + else + local kind = (node.SplitAxis == ImGuiAxis.X) and "horizontal split" or ((node.SplitAxis == ImGuiAxis.Y) and "vertical split" or "empty") + open = ImGui.TreeNodeEx(node.ID, tree_node_flags, "%s 0x%04X%s: %s (vis: '%s')", label, node.ID, node.IsVisible and "" or " (hidden)", kind, node.VisibleWindow and node.VisibleWindow.Name or "NULL") + end + if not is_alive then ImGui.PopStyleColor() end + if is_active and ImGui.IsItemHovered() then + local window = node.HostWindow or node.VisibleWindow + if window then YellowRect(window, node.Pos, node.Pos + node.Size) end + end + if open then + ImGui.BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node.Pos.x, node.Pos.y, node.Size.x, node.Size.y, node.SizeRef.x, node.SizeRef.y) + ImGui.DebugNodeWindow(node.HostWindow, "HostWindow") + ImGui.DebugNodeWindow(node.VisibleWindow, "VisibleWindow") + ImGui.BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node.SelectedTabId, node.LastFocusedNodeId) + ImGui.BulletText("Misc:%s%s%s%s%s%s%s", node:IsDockSpace() and " IsDockSpace" or "", node:IsCentralNode() and " IsCentralNode" or "", + is_alive and " IsAlive" or "", is_active and " IsActive" or "", node.IsFocused and " IsFocused" or "", node.WantLockSizeOnce and " WantLockSizeOnce" or "", node.HasCentralNodeChild and " HasCentralNodeChild" or "") + if ImGui.TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node.MergedFlags, node.LocalFlags, node.LocalFlagsInWindows, node.SharedFlags) then + if ImGui.BeginTable("flags", 4) then + ImGui.TableNextColumn(); DebugNodeDockNodeFlags(node.MergedFlags, "MergedFlags", false) + ImGui.TableNextColumn(); node.LocalFlags = DebugNodeDockNodeFlags(node.LocalFlags, "LocalFlags", true) + ImGui.TableNextColumn(); DebugNodeDockNodeFlags(node.LocalFlagsInWindows, "LocalFlagsInWindows", false) + ImGui.TableNextColumn(); node.SharedFlags = DebugNodeDockNodeFlags(node.SharedFlags, "SharedFlags", true) + ImGui.EndTable() + end + ImGui.TreePop() + end + if node.ParentNode then ImGui.DebugNodeDockNode(node.ParentNode, "ParentNode") end + if node.ChildNodes[0] then ImGui.DebugNodeDockNode(node.ChildNodes[0], "Child[0]") end + if node.ChildNodes[1] then ImGui.DebugNodeDockNode(node.ChildNodes[1], "Child[1]") end + if node.TabBar then ImGui.DebugNodeTabBar(node.TabBar, "TabBar") end + ImGui.DebugNodeWindowsList(node.Windows, "Windows") + ImGui.TreePop() + end +end + +function ImGui.DebugNodeWindow(window, label) + if window == nil then + ImGui.BulletText("%s: NULL", label) + return + end + local g = ImGui.GetCurrentContext() + local is_active = window.WasActive + local tree_node_flags = (window == g.NavWindow) and ImGuiTreeNodeFlags.Selected or ImGuiTreeNodeFlags.None + if not is_active then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local open = ImGui.TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window.Name, is_active and "" or " *Inactive*") + if not is_active then ImGui.PopStyleColor() end + if ImGui.IsItemHovered() and is_active then YellowRect(window, window.Pos, window.Pos + window.Size) end + if not open then return end + if window.MemoryCompacted then ImGui.TextDisabled("Note: some memory buffers have been compacted/freed.") end + local W = ImGuiWindowFlags + local flags = window.Flags + local function F(f, s) return (bit32.band(flags, f) ~= 0) and s or "" end + ImGui.DebugNodeDrawList(window, window.Viewport, window.DrawList, "DrawList") + ImGui.BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window.Pos.x, window.Pos.y, window.Size.x, window.Size.y, window.ContentSize.x, window.ContentSize.y, window.ContentSizeIdeal.x, window.ContentSizeIdeal.y) + ImGui.BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, F(W.ChildWindow, "Child "), F(W.Tooltip, "Tooltip "), F(W.Popup, "Popup "), F(W.Modal, "Modal "), F(W.ChildMenu, "ChildMenu "), + F(W.NoSavedSettings, "NoSavedSettings "), F(W.NoMouseInputs, "NoMouseInputs"), F(W.NoNavInputs, "NoNavInputs"), F(W.AlwaysAutoResize, "AlwaysAutoResize")) + if bit32.band(flags, W.ChildWindow) ~= 0 then + local cf = window.ChildFlags or 0 + local C = ImGuiChildFlags + ImGui.BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", cf, (bit32.band(cf, C.Borders) ~= 0) and "Borders " or "", (bit32.band(cf, C.ResizeX) ~= 0) and "ResizeX " or "", + (bit32.band(cf, C.ResizeY) ~= 0) and "ResizeY " or "", (bit32.band(cf, C.NavFlattened) ~= 0) and "NavFlattened " or "") + end + ImGui.BulletText("WindowClassId: 0x%08X", window.WindowClass and window.WindowClass.ClassId or 0) + ImGui.BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window.Scroll.x, window.ScrollMax.x, window.Scroll.y, window.ScrollMax.y, window.ScrollbarX and "X" or "", window.ScrollbarY and "Y" or "") + ImGui.BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", B(window.Active), B(window.WasActive), B(window.WriteAccessed), (window.Active or window.WasActive) and window.BeginOrderWithinContext or -1) + ImGui.BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", B(window.Appearing), B(window.Hidden), window.HiddenFramesCanSkipItems, window.HiddenFramesCannotSkipItems, B(window.SkipItems)) + for layer = 0, 1 do + local r = window.NavRectRel[layer] + if r.Min.x >= r.Max.x and r.Min.y >= r.Max.y then + ImGui.BulletText("NavLastIds[%d]: 0x%08X", layer, window.NavLastIds[layer]) + else + ImGui.BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window.NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y) + end + end + ImGui.BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window.DC.NavLayersActiveMask, window.NavLastChildNavWindow and window.NavLastChildNavWindow.Name or "NULL") + ImGui.BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window.Viewport and (window.Viewport.Idx or 0) or -1, window.ViewportOwned and " (Owned)" or "", window.ViewportId or 0, window.ViewportPos.x, window.ViewportPos.y) + ImGui.BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window.DockId or 0, window.DockOrder or -1, B(window.DockIsActive), B(window.DockTabIsVisible)) + if window.DockNode or window.DockNodeAsHost then + ImGui.DebugNodeDockNode(window.DockNodeAsHost or window.DockNode, window.DockNodeAsHost and "DockNodeAsHost" or "DockNode") + end + if window.RootWindow ~= window then ImGui.DebugNodeWindow(window.RootWindow, "RootWindow") end + if window.RootWindowDockTree and window.RootWindowDockTree ~= window.RootWindow then ImGui.DebugNodeWindow(window.RootWindowDockTree, "RootWindowDockTree") end + if window.ParentWindow ~= nil then ImGui.DebugNodeWindow(window.ParentWindow, "ParentWindow") end + if window.ParentWindowForFocusRoute ~= nil then ImGui.DebugNodeWindow(window.ParentWindowForFocusRoute, "ParentWindowForFocusRoute") end + if window.DC.ChildWindows.Size > 0 then ImGui.DebugNodeWindowsList(window.DC.ChildWindows, "ChildWindows") end + if window.ColumnsStorage.Size > 0 and ImGui.TreeNode("Columns", "Columns sets (%d)", window.ColumnsStorage.Size) then + for _, columns in window.ColumnsStorage:iter() do ImGui.DebugNodeColumns(columns) end + ImGui.TreePop() + end + ImGui.DebugNodeStorage(window.StateStorage, "Storage") + ImGui.TreePop() +end + +function ImGui.DebugNodeWindowSettings(settings) + if settings.WantDelete then ImGui.BeginDisabled() end + ImGui.BulletText("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", settings.ID, settings.Name or "", settings.Pos.x, settings.Pos.y, settings.Size.x, settings.Size.y, B(settings.Collapsed)) + if settings.WantDelete then ImGui.EndDisabled() end +end + +function ImGui.DebugNodeWindowsList(windows, label) + if not ImGui.TreeNode(label, "%s (%d)", label, windows.Size) then return end + for i = windows.Size, 1, -1 do + ImGui.PushID(tostring(windows.Data[i])) + ImGui.DebugNodeWindow(windows.Data[i], "Window") + ImGui.PopID() + end + ImGui.TreePop() +end + +function ImGui.DebugNodeWindowsListByBeginStackParent(windows, first, last, parent_in_begin_stack) + for i = first, last do + local window = windows[i] + if window.ParentWindowInBeginStack == parent_in_begin_stack then + local buf = string.format("[%04d] Window", window.BeginOrderWithinContext) + ImGui.DebugNodeWindow(window, buf) + ImGui.TreePush(buf) + ImGui.DebugNodeWindowsListByBeginStackParent(windows, i + 1, last, window) + ImGui.TreePop() + end + end +end + +function ImGui.DebugNodeViewport(viewport) + local g = ImGui.GetCurrentContext() + ImGui.SetNextItemOpen(true, ImGuiCond.Once) + local open = ImGui.TreeNode(viewport.ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport.Idx or 0, viewport.ID, viewport.ParentViewportId or 0, viewport.Window and viewport.Window.Name or "N/A") + if ImGui.IsItemHovered() then g.DebugMetricsConfig.HighlightViewportID = viewport.ID end + if open then + ImGui.BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nFrameBufferScale: (%.2f,%.2f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", + viewport.Pos.x, viewport.Pos.y, viewport.Size.x, viewport.Size.y, viewport.FramebufferScale and viewport.FramebufferScale.x or 1, viewport.FramebufferScale and viewport.FramebufferScale.y or 1, + viewport.WorkInsetMin.x, viewport.WorkInsetMin.y, viewport.WorkInsetMax.x, viewport.WorkInsetMax.y, viewport.PlatformMonitor or 0, (viewport.DpiScale or 1) * 100.0) + ImGui.BulletText("Flags: 0x%04X", viewport.Flags or 0) + if viewport.DrawDataP then + for _, draw_list in viewport.DrawDataP.CmdLists:iter() do ImGui.DebugNodeDrawList(nil, viewport, draw_list, "DrawList") end + end + ImGui.TreePop() + end +end + +function ImGui.DebugTextEncoding(str) + ImGui.Text("Text: \"%s\"", str) + if not ImGui.BeginTable("##DebugTextEncoding", 4, bit32.bor(ImGuiTableFlags.Borders, ImGuiTableFlags.RowBg, ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.Resizable)) then return end + ImGui.TableSetupColumn("Offset") + ImGui.TableSetupColumn("UTF-8") + ImGui.TableSetupColumn("Glyph") + ImGui.TableSetupColumn("Codepoint") + ImGui.TableHeadersRow() + for pos, c in utf8.codes(str) do + local nxt = utf8.offset(str, 2, pos) or (#str + 1) + ImGui.TableNextColumn() + ImGui.Text("%d", pos - 1) + ImGui.TableNextColumn() + for b = pos, nxt - 1 do + if b > pos then ImGui.SameLine() end + ImGui.Text("0x%02X", string.byte(str, b)) + end + ImGui.TableNextColumn() + ImGui.TextUnformatted(string.sub(str, pos, nxt - 1)) + ImGui.TableNextColumn() + ImGui.Text("U+%04X", c) + end + ImGui.EndTable() +end + +local WRT_Names = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" } +local TRT_Names = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", + "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" } +local TRT_ColumnsRect = 6 + +local function GetWindowRect(window, t) + if t == 0 then return window:Rect() + elseif t == 1 then return window.OuterRectClipped + elseif t == 2 then return window.InnerRect + elseif t == 3 then return window.InnerClipRect + elseif t == 4 then return window.WorkRect + elseif t == 5 then local m = window.InnerRect.Min - window.Scroll + window.WindowPadding; return ImRect(m, m + window.ContentSize) + elseif t == 6 then local m = window.InnerRect.Min - window.Scroll + window.WindowPadding; return ImRect(m, m + window.ContentSizeIdeal) + else return window.ContentRegionRect end +end + +local function GetTableRect(tbl, t, n) + local inst = ImGui.TableGetInstanceData(tbl, tbl.InstanceCurrent) + if t == 0 then return tbl.OuterRect elseif t == 1 then return tbl.InnerRect elseif t == 2 then return tbl.WorkRect + elseif t == 3 then return tbl.HostClipRect elseif t == 4 then return tbl.InnerClipRect elseif t == 5 then return tbl.BgClipRect end + local c = tbl.Columns[n] + if t == 6 then return ImRect(c.MinX, tbl.InnerClipRect.Min.y, c.MaxX, tbl.InnerClipRect.Min.y + inst.LastOuterHeight) + elseif t == 7 then return ImRect(c.WorkMinX, tbl.WorkRect.Min.y, c.WorkMaxX, tbl.WorkRect.Max.y) + elseif t == 8 then return c.ClipRect + elseif t == 9 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXHeadersUsed, tbl.InnerClipRect.Min.y + inst.LastTopHeadersRowHeight) + elseif t == 10 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXHeadersIdeal, tbl.InnerClipRect.Min.y + inst.LastTopHeadersRowHeight) + elseif t == 11 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXFrozen, tbl.InnerClipRect.Min.y + inst.LastFrozenHeight) + else return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y + inst.LastFrozenHeight, c.ContentMaxXUnfrozen, tbl.InnerClipRect.Max.y) end +end + +local function ForEachTable(g, fn) + for i = 0, g.Tables:GetMapSize() - 1 do + local t = g.Tables:GetByIndex(i) + if t then fn(t) end + end +end + +local MetricsEncodingBuf = nil +local MetricsDockRootOnly = true +function ImGui.ShowMetricsWindow(p_open) + local g = ImGui.GetCurrentContext() + local io = g.IO + local cfg = g.DebugMetricsConfig + if cfg.ShowDebugLog then cfg.ShowDebugLog = ImGui.ShowDebugLogWindow(cfg.ShowDebugLog) end + if cfg.ShowIDStackTool then cfg.ShowIDStackTool = ImGui.ShowIDStackToolWindow(cfg.ShowIDStackTool) end + + local visible + p_open, visible = ImGui.Begin("ImGui Sincerely Metrics/Debugger", p_open) + if not visible or ImGui.GetCurrentWindow().BeginCount > 1 then + ImGui.End() + return p_open + end + + ImGui.Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM) + ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / math.max(io.Framerate, 0.0001), io.Framerate) + ImGui.Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, math.floor(io.MetricsRenderIndices / 3)) + ImGui.Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount) + ImGui.Separator() + + if cfg.ShowWindowsRectsType == nil or cfg.ShowWindowsRectsType < 0 then cfg.ShowWindowsRectsType = 4 end + if cfg.ShowTablesRectsType == nil or cfg.ShowTablesRectsType < 0 then cfg.ShowTablesRectsType = 2 end + + -- Tools + if ImGui.TreeNode("Tools") then + ImGui.SeparatorText("Visualize") + _, cfg.ShowDebugLog = ImGui.Checkbox("Show Debug Log", cfg.ShowDebugLog == true) + ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.") + _, cfg.ShowIDStackTool = ImGui.Checkbox("Show ID Stack Tool", cfg.ShowIDStackTool == true) + ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.") + _, cfg.ShowWindowsBeginOrder = ImGui.Checkbox("Show windows begin order", cfg.ShowWindowsBeginOrder == true) + _, cfg.ShowWindowsRects = ImGui.Checkbox("Show windows rectangles", cfg.ShowWindowsRects == true) + ImGui.SameLine() + ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12) + local changed + cfg.ShowWindowsRectsType, changed = ImGui.Combo("##show_windows_rect_type", cfg.ShowWindowsRectsType, WRT_Names, #WRT_Names, #WRT_Names) + cfg.ShowWindowsRects = cfg.ShowWindowsRects or changed + if cfg.ShowWindowsRects and g.NavWindow ~= nil then + ImGui.BulletText("'%s':", g.NavWindow.Name) + ImGui.Indent() + for rect_n = 0, #WRT_Names - 1 do + local r = GetWindowRect(g.NavWindow, rect_n) + ImGui.Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r:GetWidth(), r:GetHeight(), WRT_Names[rect_n + 1]) + end + ImGui.Unindent() + end + _, cfg.ShowTablesRects = ImGui.Checkbox("Show tables rectangles", cfg.ShowTablesRects == true) + ImGui.SameLine() + ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12) + cfg.ShowTablesRectsType, changed = ImGui.Combo("##show_table_rects_type", cfg.ShowTablesRectsType, TRT_Names, #TRT_Names, #TRT_Names) + cfg.ShowTablesRects = cfg.ShowTablesRects or changed + _, g.DebugShowGroupRects = ImGui.Checkbox("Show groups rectangles", g.DebugShowGroupRects == true) + + ImGui.SeparatorText("Validate") + _, io.ConfigDebugBeginReturnValueLoop = ImGui.Checkbox("Debug Begin/BeginChild return value", io.ConfigDebugBeginReturnValueLoop == true) + ImGui.SameLine(); MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.") + _, cfg.ShowTextEncodingViewer = ImGui.Checkbox("UTF-8 Encoding viewer", cfg.ShowTextEncodingViewer == true) + ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.") + if cfg.ShowTextEncodingViewer then + MetricsEncodingBuf = MetricsEncodingBuf or DemoBuf("", 64) + ImGui.SetNextItemWidth(-FLT_MIN) + ImGui.InputText("##DebugTextEncodingBuf", MetricsEncodingBuf, 64) + local str = DemoBufStr(MetricsEncodingBuf) + if str ~= "" then ImGui.DebugTextEncoding(str) end + end + ImGui.TreePop() + end + + -- Windows + if ImGui.TreeNode("Windows", "Windows (%d)", g.Windows.Size) then + ImGui.DebugNodeWindowsList(g.Windows, "By display order") + ImGui.DebugNodeWindowsList(g.WindowsFocusOrder, "By focus order (root windows)") + if ImGui.TreeNode("By submission order (begin stack)") then + local tmp = {} + for _, window in g.Windows:iter() do + if window.LastFrameActive + 1 >= g.FrameCount then tmp[#tmp + 1] = window end + end + table.sort(tmp, function(a, b) return a.BeginOrderWithinContext < b.BeginOrderWithinContext end) + ImGui.DebugNodeWindowsListByBeginStackParent(tmp, 1, #tmp, nil) + ImGui.TreePop() + end + ImGui.TreePop() + end + + -- DrawLists + local drawlist_count = 0 + for _, viewport in g.Viewports:iter() do drawlist_count = drawlist_count + viewport.DrawDataP.CmdLists.Size end + if ImGui.TreeNode("DrawLists", "DrawLists (%d)", drawlist_count) then + _, cfg.ShowDrawCmdMesh = ImGui.Checkbox("Show ImDrawCmd mesh when hovering", cfg.ShowDrawCmdMesh ~= false) + _, cfg.ShowDrawCmdBoundingBoxes = ImGui.Checkbox("Show ImDrawCmd bounding boxes when hovering", cfg.ShowDrawCmdBoundingBoxes ~= false) + for _, viewport in g.Viewports:iter() do + local has = false + for _, draw_list in viewport.DrawDataP.CmdLists:iter() do + if not has then ImGui.Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport.Idx or 0, viewport.ID) end + has = true + ImGui.DebugNodeDrawList(nil, viewport, draw_list, "DrawList") + end + end + ImGui.TreePop() + end + + -- Viewports + if ImGui.TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size) then + ImGui.SetNextItemOpen(true, ImGuiCond.Once) + if ImGui.TreeNode("Windows Minimap") then + ImGui.RenderViewportsThumbnails() + ImGui.TreePop() + end + cfg.HighlightViewportID = 0 + for _, viewport in g.Viewports:iter() do ImGui.DebugNodeViewport(viewport) end + ImGui.TreePop() + end + + -- Fonts + for i, atlas in g.FontAtlases:iter() do + if ImGui.TreeNode(tostring(atlas), "Fonts (%d), Textures (%d)", atlas.Fonts.Size, atlas.TexList.Size) then + ImGui.ShowFontAtlas(atlas) + ImGui.TreePop() + end + end + + -- Popups + if ImGui.TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size) then + for _, popup_data in g.OpenPopupStack:iter() do + local window = popup_data.Window + local W = ImGuiWindowFlags + ImGui.BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'", popup_data.PopupId, window and window.Name or "NULL", + (window and bit32.band(window.Flags, W.ChildWindow) ~= 0) and "Child;" or "", (window and bit32.band(window.Flags, W.ChildMenu) ~= 0) and "Menu;" or "", + popup_data.RestoreNavWindow and popup_data.RestoreNavWindow.Name or "NULL", (window and window.ParentWindow) and window.ParentWindow.Name or "NULL") + end + ImGui.TreePop() + end + + -- TabBars + if ImGui.TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.AliveCount) then + for n = 0, g.TabBars:GetMapSize() - 1 do + local tab_bar = g.TabBars:GetByIndex(n) + if tab_bar then + ImGui.PushID(n) + ImGui.DebugNodeTabBar(tab_bar, "TabBar") + ImGui.PopID() + end + end + ImGui.TreePop() + end + + -- Tables + if ImGui.TreeNode("Tables", "Tables (%d)", g.Tables.AliveCount) then + ForEachTable(g, ImGui.DebugNodeTable) + ImGui.TreePop() + end + + -- Docking + if ImGui.TreeNode("Docking") then + local dc = g.DockContext + _, MetricsDockRootOnly = ImGui.Checkbox("List root nodes", MetricsDockRootOnly) + _, cfg.ShowDockingNodes = ImGui.Checkbox("Ctrl shows window dock info", cfg.ShowDockingNodes == true) + if ImGui.SmallButton("Clear nodes") then ImGui.DockContextClearNodes(g, 0, true) end + ImGui.SameLine() + if ImGui.SmallButton("Rebuild all") then dc.WantFullRebuild = true end + local ids = {} + for id in pairs(dc.Nodes) do ids[#ids + 1] = id end + table.sort(ids) + for _, id in ipairs(ids) do + local node = dc.Nodes[id] + if node and (not MetricsDockRootOnly or node:IsRootNode()) then ImGui.DebugNodeDockNode(node, "Node") end + end + ImGui.TreePop() + end + + -- Settings + if ImGui.TreeNode("Settings") then + if ImGui.SmallButton("Clear") then ImGui.ClearIniSettings() end + ImGui.SameLine() + if ImGui.SmallButton("Save to memory") then ImGui.SaveIniSettingsToMemory() end + ImGui.SameLine() + ImGui.TextUnformatted(io.IniFilename and ("\"" .. io.IniFilename .. "\"") or "") + ImGui.Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer) + if ImGui.TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size) then + for _, handler in g.SettingsHandlers:iter() do ImGui.BulletText("\"%s\"", handler.TypeName) end + ImGui.TreePop() + end + if ImGui.TreeNode("SettingsWindows", "Settings packed data: Windows: %d entries", g.SettingsWindows.Size) then + for _, settings in g.SettingsWindows:iter() do ImGui.DebugNodeWindowSettings(settings) end + ImGui.TreePop() + end + if ImGui.TreeNode("SettingsTables", "Settings packed data: Tables: %d entries", g.SettingsTables.Size) then + for _, settings in g.SettingsTables:iter() do ImGui.DebugNodeTableSettings(settings, nil) end + ImGui.TreePop() + end + if ImGui.TreeNode("SettingsDocking", "Settings packed data: Docking") then + local dc = g.DockContext + ImGui.Text("In SettingsWindows:") + for _, settings in g.SettingsWindows:iter() do + if (settings.DockId or 0) ~= 0 then ImGui.BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings.Name or "", settings.DockId, settings.DockOrder or -1) end + end + ImGui.Text("In SettingsNodes:") + for _, settings in dc.NodesSettings:iter() do + local selected_tab_name = nil + if (settings.SelectedTabId or 0) ~= 0 then + local window = ImGui.FindWindowByID(settings.SelectedTabId) + selected_tab_name = window and window.Name or nil + end + ImGui.BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings.ID, settings.ParentNodeId or 0, settings.SelectedTabId or 0, selected_tab_name or ((settings.SelectedTabId or 0) ~= 0 and "N/A" or "")) + end + ImGui.TreePop() + end + local ini = g.SettingsIniData or "" + if ImGui.TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", #ini) then + ImGui.InputTextMultiline("##Ini", DemoBuf(ini, #ini + 1), #ini + 1, ImVec2(-FLT_MIN, ImGui.GetTextLineHeight() * 20), ImGuiInputTextFlags.ReadOnly) + ImGui.TreePop() + end + ImGui.TreePop() + end + + -- Inputs + if ImGui.TreeNode("Inputs") then + ImGui.Text("KEYBOARD/GAMEPAD/MOUSE KEYS") + ImGui.Indent() + ImGui.Text("Keys down:") + for key = ImGuiKey.NamedKey_BEGIN, ImGuiKey.NamedKey_END - 1 do + if ImGui.IsKeyDown(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end + end + ImGui.Text("Keys pressed:") + for key = ImGuiKey.NamedKey_BEGIN, ImGuiKey.NamedKey_END - 1 do + if ImGui.IsKeyPressed(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end + end + ImGui.Text("Keys released:") + for key = ImGuiKey.NamedKey_BEGIN, ImGuiKey.NamedKey_END - 1 do + if ImGui.IsKeyReleased(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end + end + ImGui.Text("Keys mods: %s%s%s%s", io.KeyCtrl and "Ctrl " or "", io.KeyShift and "Shift " or "", io.KeyAlt and "Alt " or "", io.KeySuper and "Super " or "") + ImGui.Unindent() + ImGui.Text("MOUSE STATE") + ImGui.Indent() + if ImGui.IsMousePosValid() then ImGui.Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y) else ImGui.Text("Mouse pos: ") end + ImGui.Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y) + ImGui.Text("Mouse down:") + for i = 0, 2 do if ImGui.IsMouseDown(i) then ImGui.SameLine(); ImGui.Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]) end end + ImGui.Text("Mouse clicked:") + for i = 0, 2 do if ImGui.IsMouseClicked(i) then ImGui.SameLine(); ImGui.Text("b%d (%d)", i, io.MouseClickedCount[i]) end end + ImGui.Text("Mouse released:") + for i = 0, 2 do if ImGui.IsMouseReleased(i) then ImGui.SameLine(); ImGui.Text("b%d", i) end end + ImGui.Text("Mouse wheel: %.1f", io.MouseWheel) + ImGui.Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer) + ImGui.Unindent() + ImGui.Text("MOUSE WHEELING") + ImGui.Indent() + ImGui.Text("WheelingWindow: '%s'", g.WheelingWindow and g.WheelingWindow.Name or "NULL") + ImGui.Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer or 0) + ImGui.Text("WheelingAxisAvg[] = { %.3f, %.3f }", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y) + ImGui.Unindent() + ImGui.TreePop() + end + + -- Internal state + if ImGui.TreeNode("Internal state") then + local function N(w) return w and w.Name or "NULL" end + ImGui.Text("WINDOWING") + ImGui.Indent() + ImGui.Text("HoveredWindow: '%s'", N(g.HoveredWindow)) + ImGui.Text("HoveredWindow->Root: '%s'", g.HoveredWindow and N(g.HoveredWindow.RootWindowDockTree) or "NULL") + ImGui.Text("HoveredWindowUnderMovingWindow: '%s'", N(g.HoveredWindowUnderMovingWindow)) + ImGui.Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode and g.DebugHoveredDockNode.ID or 0) + ImGui.Text("MovingWindow: '%s'", N(g.MovingWindow)) + ImGui.Unindent() + ImGui.Text("ITEMS") + ImGui.Indent() + ImGui.Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, B(g.ActiveIdAllowOverlap)) + ImGui.Text("ActiveIdWindow: '%s'", N(g.ActiveIdWindow)) + ImGui.Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, B(g.HoveredIdAllowOverlap)) + ImGui.Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId or 0, g.HoverItemDelayTimer or 0, g.HoverItemDelayClearTimer or 0) + ImGui.Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\"", B(g.DragDropActive), g.DragDropPayload.SourceId or 0, tostring(g.DragDropPayload.DataType or "")) + ImGui.Unindent() + ImGui.Text("NAV,FOCUS") + ImGui.Indent() + ImGui.Text("NavWindow: '%s'", N(g.NavWindow)) + ImGui.Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer) + ImGui.Text("NavActive: %d, NavVisible: %d", B(io.NavActive), B(io.NavVisible)) + ImGui.Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId or 0, g.NavActivatePressedId or 0) + ImGui.Text("NavActivateFlags: %04X", g.NavActivateFlags or 0) + ImGui.Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", B(g.NavCursorVisible), B(g.NavHighlightItemUnderNav)) + ImGui.Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId or 0) + ImGui.Text("NavWindowingTarget: '%s'", N(g.NavWindowingTarget)) + ImGui.Unindent() + ImGui.TreePop() + end + + -- Overlays + if cfg.ShowWindowsRects or cfg.ShowWindowsBeginOrder then + for _, window in g.Windows:iter() do + if window.WasActive then + local draw_list = FG(window) + if cfg.ShowWindowsRects then + local r = GetWindowRect(window, cfg.ShowWindowsRectsType) + draw_list:AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)) + end + if cfg.ShowWindowsBeginOrder and bit32.band(window.Flags, ImGuiWindowFlags.ChildWindow) == 0 then + local font_size = ImGui.GetFontSize() + draw_list:AddRectFilled(window.Pos, window.Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)) + draw_list:AddText(window.Pos, IM_COL32(255, 255, 255, 255), tostring(window.BeginOrderWithinContext)) + end + end + end + end + if cfg.ShowTablesRects then + ForEachTable(g, function(tbl) + if tbl.LastFrameActive < g.FrameCount - 1 then return end + local draw_list = FG(tbl.OuterWindow) + if cfg.ShowTablesRectsType >= TRT_ColumnsRect then + for column_n = 0, tbl.ColumnsCount - 1 do + local r = GetTableRect(tbl, cfg.ShowTablesRectsType, column_n) + local hot = (tbl.HoveredColumnBody == column_n) + draw_list:AddRect(r.Min, r.Max, hot and IM_COL32(255, 255, 128, 255) or IM_COL32(255, 0, 128, 255), 0.0, hot and 3.0 or 1.0) + end + else + local r = GetTableRect(tbl, cfg.ShowTablesRectsType, -1) + draw_list:AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)) + end + end) + end + + ImGui.End() + return p_open +end + return true end @@ -31283,6 +31981,7 @@ function ImGuiIO() WantTextInput = nil, Framerate = 0, + MetricsRenderVertices = 0, MetricsRenderIndices = 0, MetricsRenderWindows = 0, MetricsActiveWindows = 0, NavActive = false, NavVisible = false, diff --git a/imgui/imgui.lua b/imgui/imgui.lua index 6c79434..6b7d0ef 100644 --- a/imgui/imgui.lua +++ b/imgui/imgui.lua @@ -12669,7 +12669,7 @@ function ImGui.DebugRenderViewportThumbnail(draw_list, viewport, bb) end end -local function RenderViewportsThumbnails() +function ImGui.RenderViewportsThumbnails() local g = GImGui local window = g.CurrentWindow @@ -12694,53 +12694,6 @@ local function RenderViewportsThumbnails() ImGui.Dummy(bb_full:GetSize() * SCALE) end --- TODO: ---- @param open bool? -function ImGui.ShowMetricsWindow(open) - local g = GImGui - - local ret - open, ret = ImGui.Begin("ImGui Sincerely Metrics/Debugger", open) - if not ret or ImGui.GetCurrentWindow().BeginCount > 1 then - ImGui.End() - return open - end - - if ImGui.TreeNode(string.format("Viewports (%d)", g.Viewports.Size)) then - -- ImGui.SetNextItemOpen(true, ImGuiCond.Once) - if ImGui.TreeNode("Windows Minimap") then - RenderViewportsThumbnails() - ImGui.TreePop() - end - - ImGui.TreePop() - end - - if ImGui.TreeNode("Memory allocations") then - local info = g.DebugAllocInfo - ImGui.Text("%d current allocations", info.TotalAllocCount - info.TotalFreeCount) - ImGui.Text("Releasing selected unused buffers after: %.2f secs", g.IO.ConfigMemoryCompactTimer) - if ImGui.SmallButton("GC now") then - g.GcCompactAll = true - end - ImGui.Text("Recent frames with allocations:") - local buf_size = #info.LastEntriesBuf - for n = buf_size, 1, -1 do - local idx = (info.LastEntriesIdx - n + buf_size) % buf_size + 1 - local entry = info.LastEntriesBuf[idx] - ImGui.BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry.FrameCount, entry.AllocCount - entry.FreeCount, entry.AllocCount, entry.FreeCount) - if n == 0 then - ImGui.SameLine() - ImGui.Text("<- %d frames ago", g.FrameCount - entry.FrameCount) - end - end - - ImGui.TreePop() - end - - ImGui.End() - return open -end diff --git a/imgui/imgui_demo_tools.lua b/imgui/imgui_demo_tools.lua index 149fab0..5551a81 100644 --- a/imgui/imgui_demo_tools.lua +++ b/imgui/imgui_demo_tools.lua @@ -575,4 +575,749 @@ function ImGui.ShowIDStackToolWindow(p_open) return p_open end +---------------------------------------------------------------- +-- [SECTION] Metrics/Debugger window +---------------------------------------------------------------- + +local function B(v) return v and 1 or 0 end +local function FG(window) return ImGui.GetForegroundDrawList(window and window.Viewport or ImGui.GetMainViewport()) end +local function YellowRect(window, min, max) FG(window):AddRect(min, max, IM_COL32(255, 255, 0, 255)) end + +function ImGui.DebugNodeColumns(columns) + if not ImGui.TreeNode(columns.ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns.ID, columns.Count, columns.Flags) then return end + ImGui.BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns.OffMaxX - columns.OffMinX, columns.OffMinX, columns.OffMaxX) + for i, column in columns.Columns:iter() do + ImGui.BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", i - 1, column.OffsetNorm, ImGui.GetColumnOffsetFromNorm(columns, column.OffsetNorm)) + end + ImGui.TreePop() +end + +function ImGui.DebugNodeStorage(storage, label) + local n = 0 + for _ in pairs(storage) do n = n + 1 end + if not ImGui.TreeNode(label, "%s: %d entries", label, n) then return end + for k, v in pairs(storage) do + ImGui.BulletText("Key 0x%08X Value { %s }", tonumber(k) or 0, tostring(v)) + end + ImGui.TreePop() +end + +function ImGui.DebugNodeDrawList(window, viewport, draw_list, label) + local g = ImGui.GetCurrentContext() + local cfg = g.DebugMetricsConfig + local cmd_count = draw_list.CmdBuffer.Size + if cmd_count > 0 and draw_list.CmdBuffer.Data[cmd_count].ElemCount == 0 and draw_list.CmdBuffer.Data[cmd_count].UserCallback == nil then + cmd_count = cmd_count - 1 + end + local node_open = ImGui.TreeNode(tostring(draw_list), "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list._OwnerName or "", draw_list.VtxBuffer.Size, draw_list.IdxBuffer.Size, cmd_count) + if draw_list == ImGui.GetWindowDrawList() then + ImGui.SameLine() + ImGui.TextColored(ImVec4(1.0, 0.4, 0.4, 1.0), "CURRENTLY APPENDING") + if node_open then ImGui.TreePop() end + return + end + local fg_draw_list = viewport and ImGui.GetForegroundDrawList(viewport) or nil + if window and ImGui.IsItemHovered() and fg_draw_list then + fg_draw_list:AddRect(window.Pos, window.Pos + window.Size, IM_COL32(255, 255, 0, 255)) + end + if not node_open then return end + if window and not window.WasActive then ImGui.TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!") end + + local vtx, idx = draw_list.VtxBuffer.Data, draw_list.IdxBuffer.Data + for cmd_n = 1, cmd_count do + local pcmd = draw_list.CmdBuffer.Data[cmd_n] + if pcmd.UserCallback then + ImGui.BulletText("Callback %s", tostring(pcmd.UserCallback)) + else + local cr = pcmd.ClipRect + local open = ImGui.TreeNode(cmd_n, "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", math.floor(pcmd.ElemCount / 3), tostring(pcmd.TexRef and (pcmd.TexRef._TexID or pcmd.TexRef._TexData)), cr.x, cr.y, cr.z, cr.w) + local function ShowMesh(show_mesh, show_aabb) + if not fg_draw_list then return end + local clip_rect = ImRect(cr.x, cr.y, cr.z, cr.w) + local vtxs_rect = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX) + local base = pcmd.IdxOffset + for i = base, base + pcmd.ElemCount - 1, 3 do + local tri = {} + for n = 0, 2 do + local v = vtx[idx[i + n + 1] + pcmd.VtxOffset + 1] + if v == nil then return end + tri[n + 1] = ImVec2(v.pos.x, v.pos.y) + vtxs_rect:Add(tri[n + 1]) + end + if show_mesh then fg_draw_list:AddPolyline(tri, 3, IM_COL32(255, 255, 0, 255), 1.0, ImDrawFlags.Closed) end + end + if show_aabb then + fg_draw_list:AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)) + fg_draw_list:AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)) + end + end + if ImGui.IsItemHovered() and (cfg.ShowDrawCmdMesh or cfg.ShowDrawCmdBoundingBoxes) then ShowMesh(cfg.ShowDrawCmdMesh, cfg.ShowDrawCmdBoundingBoxes) end + if open then + ImGui.Selectable(string.format("Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d", pcmd.ElemCount, pcmd.VtxOffset, pcmd.IdxOffset)) + if ImGui.IsItemHovered() then ShowMesh(true, false) end + ImGui.TreePop() + end + end + end + ImGui.TreePop() +end + +function ImGui.DebugNodeTabBar(tab_bar, label) + local g = ImGui.GetCurrentContext() + local is_active = (tab_bar.PrevFrameVisible >= ImGui.GetFrameCount() - 2) + local names = {} + for tab_n = 1, math.min(tab_bar.Tabs.Size, 3) do + local tab = tab_bar.Tabs.Data[tab_n] + names[#names + 1] = "'" .. (ImGui.TabBarGetTabName and ImGui.TabBarGetTabName(tab_bar, tab) or "?") .. "'" + end + local buf = string.format("%s 0x%08X (%d tabs)%s { %s%s", label, tab_bar.ID, tab_bar.Tabs.Size, is_active and "" or " *Inactive*", table.concat(names, ", "), (tab_bar.Tabs.Size > 3) and " ... }" or " } ") + if not is_active then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local open = ImGui.TreeNode(label, "%s", buf) + if not is_active then ImGui.PopStyleColor() end + if is_active and ImGui.IsItemHovered() then + local dl = FG(tab_bar.Window) + dl:AddRect(tab_bar.BarRect.Min, tab_bar.BarRect.Max, IM_COL32(255, 255, 0, 255)) + dl:AddLine(ImVec2(tab_bar.ScrollingRectMinX, tab_bar.BarRect.Min.y), ImVec2(tab_bar.ScrollingRectMinX, tab_bar.BarRect.Max.y), IM_COL32(0, 255, 0, 255)) + dl:AddLine(ImVec2(tab_bar.ScrollingRectMaxX, tab_bar.BarRect.Min.y), ImVec2(tab_bar.ScrollingRectMaxX, tab_bar.BarRect.Max.y), IM_COL32(0, 255, 0, 255)) + end + if open then + for tab_n, tab in tab_bar.Tabs:iter() do + ImGui.PushID(tab_n) + if ImGui.SmallButton("<") and ImGui.TabBarQueueReorder then ImGui.TabBarQueueReorder(tab_bar, tab, -1) end ImGui.SameLine(0, 2) + if ImGui.SmallButton(">") and ImGui.TabBarQueueReorder then ImGui.TabBarQueueReorder(tab_bar, tab, 1) end ImGui.SameLine() + ImGui.Text("%02d%s Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f", tab_n - 1, (tab.ID == tab_bar.SelectedTabId) and "*" or " ", tab.ID, + ImGui.TabBarGetTabName and ImGui.TabBarGetTabName(tab_bar, tab) or "?", tab.Offset, tab.Width, tab.ContentWidth) + ImGui.PopID() + end + ImGui.TreePop() + end +end + +local function SizingDesc(f) + local TF = ImGuiTableFlags + f = bit32.band(f, TF.SizingMask_) + if f == TF.SizingFixedFit then return "FixedFit" elseif f == TF.SizingFixedSame then return "FixedSame" + elseif f == TF.SizingStretchProp then return "StretchProp" elseif f == TF.SizingStretchSame then return "StretchSame" end + return "N/A" +end + +function ImGui.DebugNodeTableSettings(settings, tbl) + local open = ImGui.TreeNode(tostring(settings), "Settings 0x%08X (%d columns)", settings.ID, settings.ColumnsCount) + local hovered = ImGui.IsItemHovered() + if hovered and tbl == nil and settings.ID ~= 0 then tbl = ImGui.TableFindByID(settings.ID) end + if hovered and tbl ~= nil then YellowRect(tbl.OuterWindow, tbl.OuterRect.Min, tbl.OuterRect.Max) end + if open then + ImGui.BulletText("SaveFlags: 0x%08X", settings.SaveFlags) + ImGui.BulletText("ColumnsCount: %d (max %d)", settings.ColumnsCount, settings.ColumnsCountMax) + for n = 0, settings.ColumnsCount - 1 do + local c = settings.Columns[n] + local dir = (c.SortOrder ~= -1) and c.SortDirection or ImGuiSortDirection.None + ImGui.BulletText("Column %d Order %d SortOrder %2d %s Vis %d %s %7.3f ID 0x%08X", n, c.DisplayOrder, c.SortOrder, + (dir == ImGuiSortDirection.Ascending) and "Asc" or ((dir == ImGuiSortDirection.Descending) and "Des" or "---"), c.IsEnabled, (c.IsStretch ~= 0) and "Weight" or "Width ", c.WidthOrWeight, c.ID) + end + ImGui.TreePop() + end +end + +function ImGui.DebugNodeTable(tbl) + local g = ImGui.GetCurrentContext() + local is_active = (tbl.LastFrameActive >= g.FrameCount - 2) + if not is_active then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local open = ImGui.TreeNode(tostring(tbl), "Table 0x%08X (%d columns, in '%s')%s", tbl.ID, tbl.ColumnsCount, tbl.OuterWindow and tbl.OuterWindow.Name or "?", is_active and "" or " *Inactive*") + if not is_active then ImGui.PopStyleColor() end + if ImGui.IsItemHovered() then YellowRect(tbl.OuterWindow, tbl.OuterRect.Min, tbl.OuterRect.Max) end + if not open then return end + if tbl.InstanceCurrent > 0 then ImGui.Text("** %d instances of same table! Some data below will refer to last instance.", tbl.InstanceCurrent + 1) end + local clear_settings = ImGui.SmallButton("Clear settings") + ImGui.BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", tbl.OuterRect.Min.x, tbl.OuterRect.Min.y, tbl.OuterRect:GetWidth(), tbl.OuterRect:GetHeight(), SizingDesc(tbl.Flags)) + ImGui.BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", tbl.ColumnsGivenWidth, tbl.ColumnsAutoFitWidth, tbl.InnerWidth, (tbl.InnerWidth == 0.0) and " (auto)" or "") + ImGui.BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", tbl.CellPaddingX, tbl.CellSpacingX1, tbl.CellSpacingX2, tbl.OuterPaddingX) + ImGui.BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", tbl.HoveredColumnBody, tbl.HoveredColumnBorder) + ImGui.BulletText("ResizedColumn: %d, HeldHeaderColumn: %d, ReorderColumn: %d", tbl.LastResizedColumn, tbl.LastHeldHeaderColumn, tbl.ReorderColumn) + for n = 0, tbl.InstanceCurrent do + local inst = ImGui.TableGetInstanceData(tbl, n) + ImGui.BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, inst.HoveredRowLast, inst.LastOuterHeight) + end + local sum_weights = 0.0 + for n = 0, tbl.ColumnsCount - 1 do + if bit32.band(tbl.Columns[n].Flags, ImGuiTableColumnFlags.WidthStretch) ~= 0 then sum_weights = sum_weights + tbl.Columns[n].StretchWeight end + end + for n = 0, tbl.ColumnsCount - 1 do + local c = tbl.Columns[n] + local CF = ImGuiTableColumnFlags + local buf = string.format( + "Column %d order %d '%s': offset %+.2f to %+.2f%s\nEnabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" .. + "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\nMinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" .. + "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\nSort: %d%s, UserData: 0x%08X, Flags: 0x%04X: %s%s%s..", + n, c.DisplayOrder, ImGui.TableGetColumnName(tbl, n) or "", c.MinX - tbl.WorkRect.Min.x, c.MaxX - tbl.WorkRect.Min.x, (n < tbl.FreezeColumnsRequest) and " (Frozen)" or "", + B(c.IsEnabled), B(c.IsVisibleX), B(c.IsVisibleY), B(c.IsRequestOutput), B(c.IsSkipItems), c.DrawChannelFrozen, c.DrawChannelUnfrozen, + c.WidthGiven, c.WidthRequest, c.WidthAuto, c.StretchWeight, (c.StretchWeight > 0.0 and sum_weights > 0) and (c.StretchWeight / sum_weights) * 100.0 or 0.0, + c.MinX, c.MaxX, c.MaxX - c.MinX, c.ClipRect.Min.x, c.ClipRect.Max.x, c.ClipRect.Max.x - c.ClipRect.Min.x, + c.ContentMaxXFrozen - c.WorkMinX, c.ContentMaxXUnfrozen - c.WorkMinX, c.ContentMaxXHeadersUsed - c.WorkMinX, c.ContentMaxXHeadersIdeal - c.WorkMinX, + c.SortOrder, (c.SortDirection == ImGuiSortDirection.Ascending) and " (Asc)" or ((c.SortDirection == ImGuiSortDirection.Descending) and " (Des)" or ""), tonumber(c.UserData) or 0, c.Flags, + (bit32.band(c.Flags, CF.WidthStretch) ~= 0) and "WidthStretch " or "", (bit32.band(c.Flags, CF.WidthFixed) ~= 0) and "WidthFixed " or "", (bit32.band(c.Flags, CF.NoResize) ~= 0) and "NoResize " or "") + ImGui.Bullet() + ImGui.Selectable(buf) + if ImGui.IsItemHovered() then YellowRect(tbl.OuterWindow, ImVec2(c.MinX, tbl.OuterRect.Min.y), ImVec2(c.MaxX, tbl.OuterRect.Max.y)) end + end + local settings = ImGui.TableGetBoundSettings(tbl) + if settings then ImGui.DebugNodeTableSettings(settings, tbl) end + if clear_settings then tbl.IsResetAllRequest = true end + ImGui.TreePop() +end + +local function DebugNodeDockNodeFlags(flags, label, enabled) + local D = ImGuiDockNodeFlags + ImGui.PushID(label) + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ImVec2(0.0, 0.0)) + ImGui.Text("%s:", label) + if not enabled then ImGui.BeginDisabled() end + for _, n in ipairs({ "NoResize", "NoResizeX", "NoResizeY", "NoTabBar", "HiddenTabBar", "NoWindowMenuButton", "NoCloseButton", "DockedWindowsInFocusRoute", + "NoDocking", "NoDockingSplit", "NoDockingSplitOther", "NoDockingOverMe", "NoDockingOverOther", "NoDockingOverEmpty", "NoUndocking" }) do + if D[n] then _, flags = ImGui.CheckboxFlags(n, flags, D[n]) end + end + if not enabled then ImGui.EndDisabled() end + ImGui.PopStyleVar() + ImGui.PopID() + return flags +end + +function ImGui.DebugNodeDockNode(node, label) + local g = ImGui.GetCurrentContext() + local is_alive = (g.FrameCount - node.LastFrameAlive < 2) + local is_active = (g.FrameCount - node.LastFrameActive < 2) + if not is_alive then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local tree_node_flags = node.IsFocused and ImGuiTreeNodeFlags.Selected or ImGuiTreeNodeFlags.None + local open + if node.Windows.Size > 0 then + open = ImGui.TreeNodeEx(node.ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node.ID, node.IsVisible and "" or " (hidden)", node.Windows.Size, node.VisibleWindow and node.VisibleWindow.Name or "NULL") + else + local kind = (node.SplitAxis == ImGuiAxis.X) and "horizontal split" or ((node.SplitAxis == ImGuiAxis.Y) and "vertical split" or "empty") + open = ImGui.TreeNodeEx(node.ID, tree_node_flags, "%s 0x%04X%s: %s (vis: '%s')", label, node.ID, node.IsVisible and "" or " (hidden)", kind, node.VisibleWindow and node.VisibleWindow.Name or "NULL") + end + if not is_alive then ImGui.PopStyleColor() end + if is_active and ImGui.IsItemHovered() then + local window = node.HostWindow or node.VisibleWindow + if window then YellowRect(window, node.Pos, node.Pos + node.Size) end + end + if open then + ImGui.BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node.Pos.x, node.Pos.y, node.Size.x, node.Size.y, node.SizeRef.x, node.SizeRef.y) + ImGui.DebugNodeWindow(node.HostWindow, "HostWindow") + ImGui.DebugNodeWindow(node.VisibleWindow, "VisibleWindow") + ImGui.BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node.SelectedTabId, node.LastFocusedNodeId) + ImGui.BulletText("Misc:%s%s%s%s%s%s%s", node:IsDockSpace() and " IsDockSpace" or "", node:IsCentralNode() and " IsCentralNode" or "", + is_alive and " IsAlive" or "", is_active and " IsActive" or "", node.IsFocused and " IsFocused" or "", node.WantLockSizeOnce and " WantLockSizeOnce" or "", node.HasCentralNodeChild and " HasCentralNodeChild" or "") + if ImGui.TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node.MergedFlags, node.LocalFlags, node.LocalFlagsInWindows, node.SharedFlags) then + if ImGui.BeginTable("flags", 4) then + ImGui.TableNextColumn(); DebugNodeDockNodeFlags(node.MergedFlags, "MergedFlags", false) + ImGui.TableNextColumn(); node.LocalFlags = DebugNodeDockNodeFlags(node.LocalFlags, "LocalFlags", true) + ImGui.TableNextColumn(); DebugNodeDockNodeFlags(node.LocalFlagsInWindows, "LocalFlagsInWindows", false) + ImGui.TableNextColumn(); node.SharedFlags = DebugNodeDockNodeFlags(node.SharedFlags, "SharedFlags", true) + ImGui.EndTable() + end + ImGui.TreePop() + end + if node.ParentNode then ImGui.DebugNodeDockNode(node.ParentNode, "ParentNode") end + if node.ChildNodes[0] then ImGui.DebugNodeDockNode(node.ChildNodes[0], "Child[0]") end + if node.ChildNodes[1] then ImGui.DebugNodeDockNode(node.ChildNodes[1], "Child[1]") end + if node.TabBar then ImGui.DebugNodeTabBar(node.TabBar, "TabBar") end + ImGui.DebugNodeWindowsList(node.Windows, "Windows") + ImGui.TreePop() + end +end + +function ImGui.DebugNodeWindow(window, label) + if window == nil then + ImGui.BulletText("%s: NULL", label) + return + end + local g = ImGui.GetCurrentContext() + local is_active = window.WasActive + local tree_node_flags = (window == g.NavWindow) and ImGuiTreeNodeFlags.Selected or ImGuiTreeNodeFlags.None + if not is_active then ImGui.PushStyleColor(ImGuiCol.Text, g.Style.Colors[ImGuiCol.TextDisabled]) end + local open = ImGui.TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window.Name, is_active and "" or " *Inactive*") + if not is_active then ImGui.PopStyleColor() end + if ImGui.IsItemHovered() and is_active then YellowRect(window, window.Pos, window.Pos + window.Size) end + if not open then return end + if window.MemoryCompacted then ImGui.TextDisabled("Note: some memory buffers have been compacted/freed.") end + local W = ImGuiWindowFlags + local flags = window.Flags + local function F(f, s) return (bit32.band(flags, f) ~= 0) and s or "" end + ImGui.DebugNodeDrawList(window, window.Viewport, window.DrawList, "DrawList") + ImGui.BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window.Pos.x, window.Pos.y, window.Size.x, window.Size.y, window.ContentSize.x, window.ContentSize.y, window.ContentSizeIdeal.x, window.ContentSizeIdeal.y) + ImGui.BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, F(W.ChildWindow, "Child "), F(W.Tooltip, "Tooltip "), F(W.Popup, "Popup "), F(W.Modal, "Modal "), F(W.ChildMenu, "ChildMenu "), + F(W.NoSavedSettings, "NoSavedSettings "), F(W.NoMouseInputs, "NoMouseInputs"), F(W.NoNavInputs, "NoNavInputs"), F(W.AlwaysAutoResize, "AlwaysAutoResize")) + if bit32.band(flags, W.ChildWindow) ~= 0 then + local cf = window.ChildFlags or 0 + local C = ImGuiChildFlags + ImGui.BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", cf, (bit32.band(cf, C.Borders) ~= 0) and "Borders " or "", (bit32.band(cf, C.ResizeX) ~= 0) and "ResizeX " or "", + (bit32.band(cf, C.ResizeY) ~= 0) and "ResizeY " or "", (bit32.band(cf, C.NavFlattened) ~= 0) and "NavFlattened " or "") + end + ImGui.BulletText("WindowClassId: 0x%08X", window.WindowClass and window.WindowClass.ClassId or 0) + ImGui.BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window.Scroll.x, window.ScrollMax.x, window.Scroll.y, window.ScrollMax.y, window.ScrollbarX and "X" or "", window.ScrollbarY and "Y" or "") + ImGui.BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", B(window.Active), B(window.WasActive), B(window.WriteAccessed), (window.Active or window.WasActive) and window.BeginOrderWithinContext or -1) + ImGui.BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", B(window.Appearing), B(window.Hidden), window.HiddenFramesCanSkipItems, window.HiddenFramesCannotSkipItems, B(window.SkipItems)) + for layer = 0, 1 do + local r = window.NavRectRel[layer] + if r.Min.x >= r.Max.x and r.Min.y >= r.Max.y then + ImGui.BulletText("NavLastIds[%d]: 0x%08X", layer, window.NavLastIds[layer]) + else + ImGui.BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window.NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y) + end + end + ImGui.BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window.DC.NavLayersActiveMask, window.NavLastChildNavWindow and window.NavLastChildNavWindow.Name or "NULL") + ImGui.BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window.Viewport and (window.Viewport.Idx or 0) or -1, window.ViewportOwned and " (Owned)" or "", window.ViewportId or 0, window.ViewportPos.x, window.ViewportPos.y) + ImGui.BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window.DockId or 0, window.DockOrder or -1, B(window.DockIsActive), B(window.DockTabIsVisible)) + if window.DockNode or window.DockNodeAsHost then + ImGui.DebugNodeDockNode(window.DockNodeAsHost or window.DockNode, window.DockNodeAsHost and "DockNodeAsHost" or "DockNode") + end + if window.RootWindow ~= window then ImGui.DebugNodeWindow(window.RootWindow, "RootWindow") end + if window.RootWindowDockTree and window.RootWindowDockTree ~= window.RootWindow then ImGui.DebugNodeWindow(window.RootWindowDockTree, "RootWindowDockTree") end + if window.ParentWindow ~= nil then ImGui.DebugNodeWindow(window.ParentWindow, "ParentWindow") end + if window.ParentWindowForFocusRoute ~= nil then ImGui.DebugNodeWindow(window.ParentWindowForFocusRoute, "ParentWindowForFocusRoute") end + if window.DC.ChildWindows.Size > 0 then ImGui.DebugNodeWindowsList(window.DC.ChildWindows, "ChildWindows") end + if window.ColumnsStorage.Size > 0 and ImGui.TreeNode("Columns", "Columns sets (%d)", window.ColumnsStorage.Size) then + for _, columns in window.ColumnsStorage:iter() do ImGui.DebugNodeColumns(columns) end + ImGui.TreePop() + end + ImGui.DebugNodeStorage(window.StateStorage, "Storage") + ImGui.TreePop() +end + +function ImGui.DebugNodeWindowSettings(settings) + if settings.WantDelete then ImGui.BeginDisabled() end + ImGui.BulletText("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", settings.ID, settings.Name or "", settings.Pos.x, settings.Pos.y, settings.Size.x, settings.Size.y, B(settings.Collapsed)) + if settings.WantDelete then ImGui.EndDisabled() end +end + +function ImGui.DebugNodeWindowsList(windows, label) + if not ImGui.TreeNode(label, "%s (%d)", label, windows.Size) then return end + for i = windows.Size, 1, -1 do + ImGui.PushID(tostring(windows.Data[i])) + ImGui.DebugNodeWindow(windows.Data[i], "Window") + ImGui.PopID() + end + ImGui.TreePop() +end + +function ImGui.DebugNodeWindowsListByBeginStackParent(windows, first, last, parent_in_begin_stack) + for i = first, last do + local window = windows[i] + if window.ParentWindowInBeginStack == parent_in_begin_stack then + local buf = string.format("[%04d] Window", window.BeginOrderWithinContext) + ImGui.DebugNodeWindow(window, buf) + ImGui.TreePush(buf) + ImGui.DebugNodeWindowsListByBeginStackParent(windows, i + 1, last, window) + ImGui.TreePop() + end + end +end + +function ImGui.DebugNodeViewport(viewport) + local g = ImGui.GetCurrentContext() + ImGui.SetNextItemOpen(true, ImGuiCond.Once) + local open = ImGui.TreeNode(viewport.ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport.Idx or 0, viewport.ID, viewport.ParentViewportId or 0, viewport.Window and viewport.Window.Name or "N/A") + if ImGui.IsItemHovered() then g.DebugMetricsConfig.HighlightViewportID = viewport.ID end + if open then + ImGui.BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nFrameBufferScale: (%.2f,%.2f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", + viewport.Pos.x, viewport.Pos.y, viewport.Size.x, viewport.Size.y, viewport.FramebufferScale and viewport.FramebufferScale.x or 1, viewport.FramebufferScale and viewport.FramebufferScale.y or 1, + viewport.WorkInsetMin.x, viewport.WorkInsetMin.y, viewport.WorkInsetMax.x, viewport.WorkInsetMax.y, viewport.PlatformMonitor or 0, (viewport.DpiScale or 1) * 100.0) + ImGui.BulletText("Flags: 0x%04X", viewport.Flags or 0) + if viewport.DrawDataP then + for _, draw_list in viewport.DrawDataP.CmdLists:iter() do ImGui.DebugNodeDrawList(nil, viewport, draw_list, "DrawList") end + end + ImGui.TreePop() + end +end + +function ImGui.DebugTextEncoding(str) + ImGui.Text("Text: \"%s\"", str) + if not ImGui.BeginTable("##DebugTextEncoding", 4, bit32.bor(ImGuiTableFlags.Borders, ImGuiTableFlags.RowBg, ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.Resizable)) then return end + ImGui.TableSetupColumn("Offset") + ImGui.TableSetupColumn("UTF-8") + ImGui.TableSetupColumn("Glyph") + ImGui.TableSetupColumn("Codepoint") + ImGui.TableHeadersRow() + for pos, c in utf8.codes(str) do + local nxt = utf8.offset(str, 2, pos) or (#str + 1) + ImGui.TableNextColumn() + ImGui.Text("%d", pos - 1) + ImGui.TableNextColumn() + for b = pos, nxt - 1 do + if b > pos then ImGui.SameLine() end + ImGui.Text("0x%02X", string.byte(str, b)) + end + ImGui.TableNextColumn() + ImGui.TextUnformatted(string.sub(str, pos, nxt - 1)) + ImGui.TableNextColumn() + ImGui.Text("U+%04X", c) + end + ImGui.EndTable() +end + +local WRT_Names = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" } +local TRT_Names = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", + "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" } +local TRT_ColumnsRect = 6 + +local function GetWindowRect(window, t) + if t == 0 then return window:Rect() + elseif t == 1 then return window.OuterRectClipped + elseif t == 2 then return window.InnerRect + elseif t == 3 then return window.InnerClipRect + elseif t == 4 then return window.WorkRect + elseif t == 5 then local m = window.InnerRect.Min - window.Scroll + window.WindowPadding; return ImRect(m, m + window.ContentSize) + elseif t == 6 then local m = window.InnerRect.Min - window.Scroll + window.WindowPadding; return ImRect(m, m + window.ContentSizeIdeal) + else return window.ContentRegionRect end +end + +local function GetTableRect(tbl, t, n) + local inst = ImGui.TableGetInstanceData(tbl, tbl.InstanceCurrent) + if t == 0 then return tbl.OuterRect elseif t == 1 then return tbl.InnerRect elseif t == 2 then return tbl.WorkRect + elseif t == 3 then return tbl.HostClipRect elseif t == 4 then return tbl.InnerClipRect elseif t == 5 then return tbl.BgClipRect end + local c = tbl.Columns[n] + if t == 6 then return ImRect(c.MinX, tbl.InnerClipRect.Min.y, c.MaxX, tbl.InnerClipRect.Min.y + inst.LastOuterHeight) + elseif t == 7 then return ImRect(c.WorkMinX, tbl.WorkRect.Min.y, c.WorkMaxX, tbl.WorkRect.Max.y) + elseif t == 8 then return c.ClipRect + elseif t == 9 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXHeadersUsed, tbl.InnerClipRect.Min.y + inst.LastTopHeadersRowHeight) + elseif t == 10 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXHeadersIdeal, tbl.InnerClipRect.Min.y + inst.LastTopHeadersRowHeight) + elseif t == 11 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXFrozen, tbl.InnerClipRect.Min.y + inst.LastFrozenHeight) + else return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y + inst.LastFrozenHeight, c.ContentMaxXUnfrozen, tbl.InnerClipRect.Max.y) end +end + +local function ForEachTable(g, fn) + for i = 0, g.Tables:GetMapSize() - 1 do + local t = g.Tables:GetByIndex(i) + if t then fn(t) end + end +end + +local MetricsEncodingBuf = nil +local MetricsDockRootOnly = true +function ImGui.ShowMetricsWindow(p_open) + local g = ImGui.GetCurrentContext() + local io = g.IO + local cfg = g.DebugMetricsConfig + if cfg.ShowDebugLog then cfg.ShowDebugLog = ImGui.ShowDebugLogWindow(cfg.ShowDebugLog) end + if cfg.ShowIDStackTool then cfg.ShowIDStackTool = ImGui.ShowIDStackToolWindow(cfg.ShowIDStackTool) end + + local visible + p_open, visible = ImGui.Begin("ImGui Sincerely Metrics/Debugger", p_open) + if not visible or ImGui.GetCurrentWindow().BeginCount > 1 then + ImGui.End() + return p_open + end + + ImGui.Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM) + ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / math.max(io.Framerate, 0.0001), io.Framerate) + ImGui.Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, math.floor(io.MetricsRenderIndices / 3)) + ImGui.Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount) + ImGui.Separator() + + if cfg.ShowWindowsRectsType == nil or cfg.ShowWindowsRectsType < 0 then cfg.ShowWindowsRectsType = 4 end + if cfg.ShowTablesRectsType == nil or cfg.ShowTablesRectsType < 0 then cfg.ShowTablesRectsType = 2 end + + -- Tools + if ImGui.TreeNode("Tools") then + ImGui.SeparatorText("Visualize") + _, cfg.ShowDebugLog = ImGui.Checkbox("Show Debug Log", cfg.ShowDebugLog == true) + ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.") + _, cfg.ShowIDStackTool = ImGui.Checkbox("Show ID Stack Tool", cfg.ShowIDStackTool == true) + ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.") + _, cfg.ShowWindowsBeginOrder = ImGui.Checkbox("Show windows begin order", cfg.ShowWindowsBeginOrder == true) + _, cfg.ShowWindowsRects = ImGui.Checkbox("Show windows rectangles", cfg.ShowWindowsRects == true) + ImGui.SameLine() + ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12) + local changed + cfg.ShowWindowsRectsType, changed = ImGui.Combo("##show_windows_rect_type", cfg.ShowWindowsRectsType, WRT_Names, #WRT_Names, #WRT_Names) + cfg.ShowWindowsRects = cfg.ShowWindowsRects or changed + if cfg.ShowWindowsRects and g.NavWindow ~= nil then + ImGui.BulletText("'%s':", g.NavWindow.Name) + ImGui.Indent() + for rect_n = 0, #WRT_Names - 1 do + local r = GetWindowRect(g.NavWindow, rect_n) + ImGui.Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r:GetWidth(), r:GetHeight(), WRT_Names[rect_n + 1]) + end + ImGui.Unindent() + end + _, cfg.ShowTablesRects = ImGui.Checkbox("Show tables rectangles", cfg.ShowTablesRects == true) + ImGui.SameLine() + ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12) + cfg.ShowTablesRectsType, changed = ImGui.Combo("##show_table_rects_type", cfg.ShowTablesRectsType, TRT_Names, #TRT_Names, #TRT_Names) + cfg.ShowTablesRects = cfg.ShowTablesRects or changed + _, g.DebugShowGroupRects = ImGui.Checkbox("Show groups rectangles", g.DebugShowGroupRects == true) + + ImGui.SeparatorText("Validate") + _, io.ConfigDebugBeginReturnValueLoop = ImGui.Checkbox("Debug Begin/BeginChild return value", io.ConfigDebugBeginReturnValueLoop == true) + ImGui.SameLine(); MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.") + _, cfg.ShowTextEncodingViewer = ImGui.Checkbox("UTF-8 Encoding viewer", cfg.ShowTextEncodingViewer == true) + ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.") + if cfg.ShowTextEncodingViewer then + MetricsEncodingBuf = MetricsEncodingBuf or DemoBuf("", 64) + ImGui.SetNextItemWidth(-FLT_MIN) + ImGui.InputText("##DebugTextEncodingBuf", MetricsEncodingBuf, 64) + local str = DemoBufStr(MetricsEncodingBuf) + if str ~= "" then ImGui.DebugTextEncoding(str) end + end + ImGui.TreePop() + end + + -- Windows + if ImGui.TreeNode("Windows", "Windows (%d)", g.Windows.Size) then + ImGui.DebugNodeWindowsList(g.Windows, "By display order") + ImGui.DebugNodeWindowsList(g.WindowsFocusOrder, "By focus order (root windows)") + if ImGui.TreeNode("By submission order (begin stack)") then + local tmp = {} + for _, window in g.Windows:iter() do + if window.LastFrameActive + 1 >= g.FrameCount then tmp[#tmp + 1] = window end + end + table.sort(tmp, function(a, b) return a.BeginOrderWithinContext < b.BeginOrderWithinContext end) + ImGui.DebugNodeWindowsListByBeginStackParent(tmp, 1, #tmp, nil) + ImGui.TreePop() + end + ImGui.TreePop() + end + + -- DrawLists + local drawlist_count = 0 + for _, viewport in g.Viewports:iter() do drawlist_count = drawlist_count + viewport.DrawDataP.CmdLists.Size end + if ImGui.TreeNode("DrawLists", "DrawLists (%d)", drawlist_count) then + _, cfg.ShowDrawCmdMesh = ImGui.Checkbox("Show ImDrawCmd mesh when hovering", cfg.ShowDrawCmdMesh ~= false) + _, cfg.ShowDrawCmdBoundingBoxes = ImGui.Checkbox("Show ImDrawCmd bounding boxes when hovering", cfg.ShowDrawCmdBoundingBoxes ~= false) + for _, viewport in g.Viewports:iter() do + local has = false + for _, draw_list in viewport.DrawDataP.CmdLists:iter() do + if not has then ImGui.Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport.Idx or 0, viewport.ID) end + has = true + ImGui.DebugNodeDrawList(nil, viewport, draw_list, "DrawList") + end + end + ImGui.TreePop() + end + + -- Viewports + if ImGui.TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size) then + ImGui.SetNextItemOpen(true, ImGuiCond.Once) + if ImGui.TreeNode("Windows Minimap") then + ImGui.RenderViewportsThumbnails() + ImGui.TreePop() + end + cfg.HighlightViewportID = 0 + for _, viewport in g.Viewports:iter() do ImGui.DebugNodeViewport(viewport) end + ImGui.TreePop() + end + + -- Fonts + for i, atlas in g.FontAtlases:iter() do + if ImGui.TreeNode(tostring(atlas), "Fonts (%d), Textures (%d)", atlas.Fonts.Size, atlas.TexList.Size) then + ImGui.ShowFontAtlas(atlas) + ImGui.TreePop() + end + end + + -- Popups + if ImGui.TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size) then + for _, popup_data in g.OpenPopupStack:iter() do + local window = popup_data.Window + local W = ImGuiWindowFlags + ImGui.BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'", popup_data.PopupId, window and window.Name or "NULL", + (window and bit32.band(window.Flags, W.ChildWindow) ~= 0) and "Child;" or "", (window and bit32.band(window.Flags, W.ChildMenu) ~= 0) and "Menu;" or "", + popup_data.RestoreNavWindow and popup_data.RestoreNavWindow.Name or "NULL", (window and window.ParentWindow) and window.ParentWindow.Name or "NULL") + end + ImGui.TreePop() + end + + -- TabBars + if ImGui.TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.AliveCount) then + for n = 0, g.TabBars:GetMapSize() - 1 do + local tab_bar = g.TabBars:GetByIndex(n) + if tab_bar then + ImGui.PushID(n) + ImGui.DebugNodeTabBar(tab_bar, "TabBar") + ImGui.PopID() + end + end + ImGui.TreePop() + end + + -- Tables + if ImGui.TreeNode("Tables", "Tables (%d)", g.Tables.AliveCount) then + ForEachTable(g, ImGui.DebugNodeTable) + ImGui.TreePop() + end + + -- Docking + if ImGui.TreeNode("Docking") then + local dc = g.DockContext + _, MetricsDockRootOnly = ImGui.Checkbox("List root nodes", MetricsDockRootOnly) + _, cfg.ShowDockingNodes = ImGui.Checkbox("Ctrl shows window dock info", cfg.ShowDockingNodes == true) + if ImGui.SmallButton("Clear nodes") then ImGui.DockContextClearNodes(g, 0, true) end + ImGui.SameLine() + if ImGui.SmallButton("Rebuild all") then dc.WantFullRebuild = true end + local ids = {} + for id in pairs(dc.Nodes) do ids[#ids + 1] = id end + table.sort(ids) + for _, id in ipairs(ids) do + local node = dc.Nodes[id] + if node and (not MetricsDockRootOnly or node:IsRootNode()) then ImGui.DebugNodeDockNode(node, "Node") end + end + ImGui.TreePop() + end + + -- Settings + if ImGui.TreeNode("Settings") then + if ImGui.SmallButton("Clear") then ImGui.ClearIniSettings() end + ImGui.SameLine() + if ImGui.SmallButton("Save to memory") then ImGui.SaveIniSettingsToMemory() end + ImGui.SameLine() + ImGui.TextUnformatted(io.IniFilename and ("\"" .. io.IniFilename .. "\"") or "") + ImGui.Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer) + if ImGui.TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size) then + for _, handler in g.SettingsHandlers:iter() do ImGui.BulletText("\"%s\"", handler.TypeName) end + ImGui.TreePop() + end + if ImGui.TreeNode("SettingsWindows", "Settings packed data: Windows: %d entries", g.SettingsWindows.Size) then + for _, settings in g.SettingsWindows:iter() do ImGui.DebugNodeWindowSettings(settings) end + ImGui.TreePop() + end + if ImGui.TreeNode("SettingsTables", "Settings packed data: Tables: %d entries", g.SettingsTables.Size) then + for _, settings in g.SettingsTables:iter() do ImGui.DebugNodeTableSettings(settings, nil) end + ImGui.TreePop() + end + if ImGui.TreeNode("SettingsDocking", "Settings packed data: Docking") then + local dc = g.DockContext + ImGui.Text("In SettingsWindows:") + for _, settings in g.SettingsWindows:iter() do + if (settings.DockId or 0) ~= 0 then ImGui.BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings.Name or "", settings.DockId, settings.DockOrder or -1) end + end + ImGui.Text("In SettingsNodes:") + for _, settings in dc.NodesSettings:iter() do + local selected_tab_name = nil + if (settings.SelectedTabId or 0) ~= 0 then + local window = ImGui.FindWindowByID(settings.SelectedTabId) + selected_tab_name = window and window.Name or nil + end + ImGui.BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings.ID, settings.ParentNodeId or 0, settings.SelectedTabId or 0, selected_tab_name or ((settings.SelectedTabId or 0) ~= 0 and "N/A" or "")) + end + ImGui.TreePop() + end + local ini = g.SettingsIniData or "" + if ImGui.TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", #ini) then + ImGui.InputTextMultiline("##Ini", DemoBuf(ini, #ini + 1), #ini + 1, ImVec2(-FLT_MIN, ImGui.GetTextLineHeight() * 20), ImGuiInputTextFlags.ReadOnly) + ImGui.TreePop() + end + ImGui.TreePop() + end + + -- Inputs + if ImGui.TreeNode("Inputs") then + ImGui.Text("KEYBOARD/GAMEPAD/MOUSE KEYS") + ImGui.Indent() + ImGui.Text("Keys down:") + for key = ImGuiKey.NamedKey_BEGIN, ImGuiKey.NamedKey_END - 1 do + if ImGui.IsKeyDown(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end + end + ImGui.Text("Keys pressed:") + for key = ImGuiKey.NamedKey_BEGIN, ImGuiKey.NamedKey_END - 1 do + if ImGui.IsKeyPressed(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end + end + ImGui.Text("Keys released:") + for key = ImGuiKey.NamedKey_BEGIN, ImGuiKey.NamedKey_END - 1 do + if ImGui.IsKeyReleased(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end + end + ImGui.Text("Keys mods: %s%s%s%s", io.KeyCtrl and "Ctrl " or "", io.KeyShift and "Shift " or "", io.KeyAlt and "Alt " or "", io.KeySuper and "Super " or "") + ImGui.Unindent() + ImGui.Text("MOUSE STATE") + ImGui.Indent() + if ImGui.IsMousePosValid() then ImGui.Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y) else ImGui.Text("Mouse pos: ") end + ImGui.Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y) + ImGui.Text("Mouse down:") + for i = 0, 2 do if ImGui.IsMouseDown(i) then ImGui.SameLine(); ImGui.Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]) end end + ImGui.Text("Mouse clicked:") + for i = 0, 2 do if ImGui.IsMouseClicked(i) then ImGui.SameLine(); ImGui.Text("b%d (%d)", i, io.MouseClickedCount[i]) end end + ImGui.Text("Mouse released:") + for i = 0, 2 do if ImGui.IsMouseReleased(i) then ImGui.SameLine(); ImGui.Text("b%d", i) end end + ImGui.Text("Mouse wheel: %.1f", io.MouseWheel) + ImGui.Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer) + ImGui.Unindent() + ImGui.Text("MOUSE WHEELING") + ImGui.Indent() + ImGui.Text("WheelingWindow: '%s'", g.WheelingWindow and g.WheelingWindow.Name or "NULL") + ImGui.Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer or 0) + ImGui.Text("WheelingAxisAvg[] = { %.3f, %.3f }", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y) + ImGui.Unindent() + ImGui.TreePop() + end + + -- Internal state + if ImGui.TreeNode("Internal state") then + local function N(w) return w and w.Name or "NULL" end + ImGui.Text("WINDOWING") + ImGui.Indent() + ImGui.Text("HoveredWindow: '%s'", N(g.HoveredWindow)) + ImGui.Text("HoveredWindow->Root: '%s'", g.HoveredWindow and N(g.HoveredWindow.RootWindowDockTree) or "NULL") + ImGui.Text("HoveredWindowUnderMovingWindow: '%s'", N(g.HoveredWindowUnderMovingWindow)) + ImGui.Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode and g.DebugHoveredDockNode.ID or 0) + ImGui.Text("MovingWindow: '%s'", N(g.MovingWindow)) + ImGui.Unindent() + ImGui.Text("ITEMS") + ImGui.Indent() + ImGui.Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, B(g.ActiveIdAllowOverlap)) + ImGui.Text("ActiveIdWindow: '%s'", N(g.ActiveIdWindow)) + ImGui.Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, B(g.HoveredIdAllowOverlap)) + ImGui.Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId or 0, g.HoverItemDelayTimer or 0, g.HoverItemDelayClearTimer or 0) + ImGui.Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\"", B(g.DragDropActive), g.DragDropPayload.SourceId or 0, tostring(g.DragDropPayload.DataType or "")) + ImGui.Unindent() + ImGui.Text("NAV,FOCUS") + ImGui.Indent() + ImGui.Text("NavWindow: '%s'", N(g.NavWindow)) + ImGui.Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer) + ImGui.Text("NavActive: %d, NavVisible: %d", B(io.NavActive), B(io.NavVisible)) + ImGui.Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId or 0, g.NavActivatePressedId or 0) + ImGui.Text("NavActivateFlags: %04X", g.NavActivateFlags or 0) + ImGui.Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", B(g.NavCursorVisible), B(g.NavHighlightItemUnderNav)) + ImGui.Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId or 0) + ImGui.Text("NavWindowingTarget: '%s'", N(g.NavWindowingTarget)) + ImGui.Unindent() + ImGui.TreePop() + end + + -- Overlays + if cfg.ShowWindowsRects or cfg.ShowWindowsBeginOrder then + for _, window in g.Windows:iter() do + if window.WasActive then + local draw_list = FG(window) + if cfg.ShowWindowsRects then + local r = GetWindowRect(window, cfg.ShowWindowsRectsType) + draw_list:AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)) + end + if cfg.ShowWindowsBeginOrder and bit32.band(window.Flags, ImGuiWindowFlags.ChildWindow) == 0 then + local font_size = ImGui.GetFontSize() + draw_list:AddRectFilled(window.Pos, window.Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)) + draw_list:AddText(window.Pos, IM_COL32(255, 255, 255, 255), tostring(window.BeginOrderWithinContext)) + end + end + end + end + if cfg.ShowTablesRects then + ForEachTable(g, function(tbl) + if tbl.LastFrameActive < g.FrameCount - 1 then return end + local draw_list = FG(tbl.OuterWindow) + if cfg.ShowTablesRectsType >= TRT_ColumnsRect then + for column_n = 0, tbl.ColumnsCount - 1 do + local r = GetTableRect(tbl, cfg.ShowTablesRectsType, column_n) + local hot = (tbl.HoveredColumnBody == column_n) + draw_list:AddRect(r.Min, r.Max, hot and IM_COL32(255, 255, 128, 255) or IM_COL32(255, 0, 128, 255), 0.0, hot and 3.0 or 1.0) + end + else + local r = GetTableRect(tbl, cfg.ShowTablesRectsType, -1) + draw_list:AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)) + end + end) + end + + ImGui.End() + return p_open +end + return true diff --git a/imgui/imgui_h.lua b/imgui/imgui_h.lua index b61b3e2..c8de511 100644 --- a/imgui/imgui_h.lua +++ b/imgui/imgui_h.lua @@ -1123,6 +1123,7 @@ function ImGuiIO() WantTextInput = nil, Framerate = 0, + MetricsRenderVertices = 0, MetricsRenderIndices = 0, MetricsRenderWindows = 0, MetricsActiveWindows = 0, NavActive = false, NavVisible = false, diff --git a/tools/test/scripts/t_apps.luau b/tools/test/scripts/t_apps.luau index 825e4a3..3bfb2d8 100644 --- a/tools/test/scripts/t_apps.luau +++ b/tools/test/scripts/t_apps.luau @@ -14,6 +14,7 @@ local frame = 0 game:GetService("RunService").Stepped:Connect(function() frame += 1 G.ImGui_ImplRoblox.NewFrame(); ImGui.NewFrame() + if frame == 2 then local c = ImGui.GetCurrentContext().DebugMetricsConfig; c.ShowWindowsRects = true; c.ShowWindowsBeginOrder = true; c.ShowTablesRects = true; c.ShowTablesRectsType = 6; c.ShowTextEncodingViewer = true end for _, n in ipairs(names) do local fn = G[n] if fn then if open[n] == nil then open[n] = true end; if open[n] then open[n] = fn(open[n]) end