Files
imgui-luau/imgui/imgui_demo_tools.lua
T

1324 lines
74 KiB
Lua

-- Demo tools: ShowAboutWindow(), ShowFontAtlas(), ShowFontSelector(), ShowDebugLogWindow(), ShowIDStackToolWindow()
local _
----------------------------------------------------------------
-- [SECTION] About Window / ShowAboutWindow()
----------------------------------------------------------------
local show_config_info = false
function ImGui.ShowAboutWindow(p_open)
local visible
p_open, visible = ImGui.Begin("About ImGui Sincerely", p_open, ImGuiWindowFlags.AlwaysAutoResize)
if not visible then ImGui.End(); return p_open end
ImGui.Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM)
ImGui.TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui"); ImGui.SameLine()
ImGui.TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md"); ImGui.SameLine()
ImGui.TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki"); ImGui.SameLine()
ImGui.TextLinkOpenURL("Extensions", "https://github.com/ocornut/imgui/wiki/Useful-Extensions"); ImGui.SameLine()
ImGui.TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases"); ImGui.SameLine()
ImGui.TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding")
ImGui.Separator()
ImGui.Text("(c) 2014-2026 Omar Cornut")
ImGui.Text("Developed by Omar Cornut and all Dear ImGui contributors.")
ImGui.Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information.")
ImGui.Text("If your company uses this, please consider funding the project.")
ImGui.Text("Luau/Roblox port: imgui_impl_roblox (software rasterizer into EditableImage tiles).")
_, show_config_info = ImGui.Checkbox("Config/Build Information", show_config_info)
if show_config_info then
local io = ImGui.GetIO()
local style = ImGui.GetStyle()
local copy_to_clipboard = ImGui.Button("Copy to clipboard")
local child_size = ImVec2(0, ImGui.GetTextLineHeightWithSpacing() * 18)
ImGui.BeginChild(ImGui.GetID("cfg_infos"), child_size, ImGuiChildFlags.FrameStyle)
if copy_to_clipboard then
ImGui.LogToClipboard()
ImGui.LogText("// (Copy from the next line. Keep the ``` markers for formatting.)\n")
ImGui.LogText("```cpp\n")
end
ImGui.Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM)
ImGui.Separator()
ImGui.Text("define: IMGUI_HAS_VIEWPORT")
ImGui.Text("define: IMGUI_HAS_DOCK")
ImGui.Text("runtime: Luau (%s)", _VERSION or "?")
ImGui.Separator()
ImGui.Text("io.BackendPlatformName: %s", io.BackendPlatformName or "NULL")
ImGui.Text("io.BackendRendererName: %s", io.BackendRendererName or "NULL")
ImGui.Text("io.ConfigFlags: 0x%08X", io.ConfigFlags)
local CF = ImGuiConfigFlags
for _, n in ipairs({ "NavEnableKeyboard", "NavEnableGamepad", "NoMouse", "NoMouseCursorChange", "NoKeyboard", "DockingEnable", "ViewportsEnable" }) do
if CF[n] and bit32.band(io.ConfigFlags, CF[n]) ~= 0 then ImGui.Text(" %s", n) end
end
for _, n in ipairs({ "MouseDrawCursor", "ConfigDpiScaleFonts", "ConfigDpiScaleViewports", "ConfigViewportsNoAutoMerge", "ConfigViewportsNoTaskBarIcon",
"ConfigViewportsNoDecoration", "ConfigViewportsNoDefaultParent", "ConfigDockingNoSplit", "ConfigDockingNoDockingOver", "ConfigDockingWithShift",
"ConfigDockingAlwaysTabBar", "ConfigDockingTransparentPayload", "ConfigMacOSXBehaviors", "ConfigNavMoveSetMousePos", "ConfigNavCaptureKeyboard",
"ConfigInputTextCursorBlink", "ConfigWindowsResizeFromEdges", "ConfigWindowsMoveFromTitleBarOnly" }) do
if io[n] == true then ImGui.Text("io.%s", n) end
end
if (io.ConfigMemoryCompactTimer or -1) >= 0.0 then ImGui.Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer) end
ImGui.Text("io.BackendFlags: 0x%08X", io.BackendFlags)
local BF = ImGuiBackendFlags
for _, n in ipairs({ "HasGamepad", "HasMouseCursors", "HasSetMousePos", "PlatformHasViewports", "HasMouseHoveredViewport", "HasParentViewport",
"RendererHasVtxOffset", "RendererHasTextures", "RendererHasViewports" }) do
if BF[n] and bit32.band(io.BackendFlags, BF[n]) ~= 0 then ImGui.Text(" %s", n) end
end
ImGui.Separator()
local atlas = io.Fonts
ImGui.Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", atlas.Fonts.Size, atlas.Flags or 0, atlas.TexData and atlas.TexData.Width or 0, atlas.TexData and atlas.TexData.Height or 0)
ImGui.Text("io.Fonts->FontLoaderName: %s", atlas.FontLoaderName or "NULL")
ImGui.Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y)
ImGui.Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)
ImGui.Separator()
ImGui.Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y)
ImGui.Text("style.WindowBorderSize: %.2f", style.WindowBorderSize)
ImGui.Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y)
ImGui.Text("style.FrameRounding: %.2f", style.FrameRounding)
ImGui.Text("style.FrameBorderSize: %.2f", style.FrameBorderSize)
ImGui.Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y)
ImGui.Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y)
if copy_to_clipboard then
ImGui.LogText("\n```\n")
ImGui.LogFinish()
end
ImGui.EndChild()
end
ImGui.End()
return p_open
end
----------------------------------------------------------------
-- [SECTION] Font atlas / font debugging
----------------------------------------------------------------
local function MetricsHelpMarker(desc)
ImGui.TextDisabled("(?)")
if ImGui.BeginItemTooltip() then
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0)
ImGui.TextUnformatted(desc)
ImGui.PopTextWrapPos()
ImGui.EndTooltip()
end
end
ImGui.MetricsHelpMarker = MetricsHelpMarker
local function U8(c) return (c and c > 0) and utf8.char(c) or "" end
function ImGui.ShowFontSelector(label)
local io = ImGui.GetIO()
local font_current = ImGui.GetFont()
if ImGui.BeginCombo(label, font_current:GetDebugName()) then
for i, font in io.Fonts.Fonts:iter() do
ImGui.PushID(i)
if ImGui.Selectable(font:GetDebugName(), font == font_current, ImGuiSelectableFlags.SelectOnNav) then io.FontDefault = font end
if font == font_current then ImGui.SetItemDefaultFocus() end
ImGui.PopID()
end
ImGui.EndCombo()
end
ImGui.SameLine()
MetricsHelpMarker("- Load additional fonts with io.Fonts->AddFontXXX() functions.\n- On Roblox: register the .ttf bytes with ImGui_ImplRoblox.AddFile() first.\n- Read FAQ and docs/FONTS.md for more details.")
end
function ImGui.DebugNodeFontGlyph(font, glyph)
ImGui.Text("Codepoint: U+%04X", glyph.Codepoint)
ImGui.Separator()
ImGui.Text("Visible: %d", glyph.Visible and 1 or 0)
ImGui.Text("AdvanceX: %.1f", glyph.AdvanceX)
ImGui.Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph.X0, glyph.Y0, glyph.X1, glyph.Y1)
ImGui.Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph.U0, glyph.V0, glyph.U1, glyph.V1)
if glyph.PackId >= 0 then
local r = { uv0 = ImVec2(), uv1 = ImVec2() }
if font.OwnerAtlas:GetCustomRect(glyph.PackId, r) then ImGui.Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph.PackId, r.w, r.h, r.x, r.y) end
end
ImGui.Text("SourceIdx: %d", glyph.SourceIdx)
end
function ImGui.DebugNodeFontGlyphsForSrcMask(font, baked, src_mask)
local draw_list = ImGui.GetWindowDrawList()
local glyph_col = ImGui.GetColorU32(ImGuiCol.Text)
local cell_size = baked.Size * 1
local cell_spacing = ImGui.GetStyle().ItemSpacing.y
-- [port] only scans loaded codepoints (IndexLookup range) instead of the whole 0..0xFFFF range
local max_c = baked.IndexLookup.Size - 1
local base = 0
while base <= max_c do
local count = 0
for n = 0, 255 do
local c = base + n
if baked:IsGlyphLoaded(c) then
local glyph = baked:FindGlyphNoFallback(c)
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then count = count + 1 end
end
end
if count > 0 and ImGui.TreeNode(base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, (count > 1) and "glyphs" or "glyph") then
local base_pos = ImGui.GetCursorScreenPos()
for n = 0, 255 do
local c = base + n
local cell_p1 = ImVec2(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + math.floor(n / 16) * (cell_size + cell_spacing))
local cell_p2 = ImVec2(cell_p1.x + cell_size, cell_p1.y + cell_size)
local glyph = baked:IsGlyphLoaded(c) and baked:FindGlyphNoFallback(c) or nil
draw_list:AddRect(cell_p1, cell_p2, glyph and IM_COL32(255, 255, 255, 100) or IM_COL32(255, 255, 255, 50))
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then
font:RenderChar(draw_list, cell_size, cell_p1, glyph_col, c)
if ImGui.IsMouseHoveringRect(cell_p1, cell_p2) and ImGui.BeginTooltip() then
ImGui.DebugNodeFontGlyph(font, glyph)
ImGui.EndTooltip()
end
end
end
ImGui.Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16))
ImGui.TreePop()
end
base = base + 256
end
end
function ImGui.DebugNodeFont(font)
local g = ImGui.GetCurrentContext()
local cfg = g.DebugMetricsConfig
local atlas = font.OwnerAtlas
local opened = ImGui.TreeNode(tostring(font), "Font: \"%s\": %d sources(s)", font:GetDebugName(), font.Sources.Size)
if not opened then ImGui.Indent() end
ImGui.Indent()
if cfg.ShowFontPreview then
ImGui.PushFont(font, 0.0)
ImGui.Text("The quick brown fox jumps over the lazy dog")
ImGui.PopFont()
end
if not opened then
ImGui.Unindent()
ImGui.Unindent()
return
end
if ImGui.SmallButton("Set as default") then ImGui.GetIO().FontDefault = font end
ImGui.SameLine()
ImGui.BeginDisabled(atlas.Fonts.Size <= 1 or atlas.Locked == true or atlas.RemoveFont == nil)
if ImGui.SmallButton("Remove") and atlas.RemoveFont then atlas:RemoveFont(font) end
ImGui.EndDisabled()
ImGui.SameLine()
if ImGui.SmallButton("Clear bakes") then ImFontAtlasFontDiscardBakes(atlas, font, 0) end
ImGui.SameLine()
if ImGui.SmallButton("Clear unused") then ImFontAtlasFontDiscardBakes(atlas, font, 2) end
ImGui.Text("Fallback character: '%s' (U+%04X)", U8(font.FallbackChar), font.FallbackChar or 0)
ImGui.Text("Ellipsis character: '%s' (U+%04X)", U8(font.EllipsisChar), font.EllipsisChar or 0)
for src_n, src in font.Sources:iter() do
if ImGui.TreeNode(tostring(src), "Input %d: '%s' [%d], Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)",
src_n - 1, src.Name or "", src.FontNo or 0, src.OversampleH or 0, src.OversampleV or 0, src.PixelSnapH and 1 or 0, src.GlyphOffset.x, src.GlyphOffset.y) then
local loader = src.FontLoader or atlas.FontLoader
ImGui.Text("Loader: '%s'", (loader and loader.Name) or "N/A")
ImGui.TreePop()
end
end
local builder = atlas.Builder
if builder then
for _, baked in builder.BakedPool:iter() do
if baked.OwnerFont == font then
ImGui.PushID(baked.BakedId)
if ImGui.TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked.Size, baked.RasterizerDensity, baked.Glyphs.Size,
(baked.LastUsedFrame < (builder.FrameCount or 0) - 1) and " *Unused*" or "") then
if ImGui.SmallButton("Load all") then
for c = 0, 0x7F do baked:FindGlyph(c) end -- [port] ASCII only: loading the whole BMP in Luau stalls for seconds
end
local surface_sqrt = math.floor(math.sqrt(baked.MetricsTotalSurface))
ImGui.Text("Ascent: %f, Descent: %f, Ascent-Descent: %f", baked.Ascent, baked.Descent, baked.Ascent - baked.Descent)
ImGui.Text("Texture Area: about %d px ~%dx%d px", baked.MetricsTotalSurface, surface_sqrt, surface_sqrt)
for src_n, src in font.Sources:iter() do
ImGui.BulletText("Input %d: '%s', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
src_n - 1, src.Name or "", src.OversampleH or 0, src.OversampleV or 0, src.PixelSnapH and 1 or 0, src.GlyphOffset.x, src.GlyphOffset.y)
end
ImGui.DebugNodeFontGlyphsForSrcMask(font, baked, bit32.bnot(0))
ImGui.TreePop()
end
ImGui.PopID()
end
end
end
ImGui.TreePop()
ImGui.Unindent()
end
local StatusNames = { [0] = "OK", "Destroyed", "WantCreate", "WantUpdates", "WantDestroy" }
local FormatNames = { [0] = "RGBA32", "Alpha8" }
function ImGui.DebugNodeTexture(tex, int_id, highlight_rect)
local g = ImGui.GetCurrentContext()
ImGui.PushID(int_id)
if ImGui.TreeNode("", "Texture #%03d (%dx%d pixels)", tex.UniqueID or 0, tex.Width, tex.Height) then
local cfg = g.DebugMetricsConfig
_, cfg.ShowTextureUsedRect = ImGui.Checkbox("Show used rect", cfg.ShowTextureUsedRect)
ImGui.PushStyleVar(ImGuiStyleVar.ImageBorderSize, math.max(1.0, g.Style.ImageBorderSize))
local p = ImGui.GetCursorScreenPos()
if tex.Status == ImTextureStatus.WantDestroy or tex.Status == ImTextureStatus.Destroyed then
ImGui.Dummy(ImVec2(tex.Width, tex.Height))
else
local ref = ImTextureRef(); ref._TexData = tex
ImGui.ImageWithBg(ref, ImVec2(tex.Width, tex.Height), ImVec2(0.0, 0.0), ImVec2(1.0, 1.0), ImVec4(0.0, 0.0, 0.0, 1.0))
end
if cfg.ShowTextureUsedRect and tex.UsedRect then
local u = tex.UsedRect
ImGui.GetWindowDrawList():AddRect(ImVec2(p.x + u.x, p.y + u.y), ImVec2(p.x + u.x + u.w, p.y + u.y + u.h), IM_COL32(255, 0, 255, 255))
end
if highlight_rect ~= nil then
local r_outer = ImRect(p.x, p.y, p.x + tex.Width, p.y + tex.Height)
local r_inner = ImRect(p.x + highlight_rect.x, p.y + highlight_rect.y, p.x + highlight_rect.x + highlight_rect.w, p.y + highlight_rect.y + highlight_rect.h)
ImGui.RenderRectFilledWithHole(ImGui.GetWindowDrawList(), r_outer, r_inner, IM_COL32(0, 0, 0, 100), 0.0)
ImGui.GetWindowDrawList():AddRect(r_inner.Min - ImVec2(1, 1), r_inner.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255))
end
ImGui.PopStyleVar()
ImGui.Text("Status = %s (%d), Format = %s (%d), UseColors = %d", StatusNames[tex.Status] or "?", tex.Status, FormatNames[tex.Format] or "?", tex.Format, tex.UseColors and 1 or 0)
ImGui.Text("TexID = %s", tostring(tex.TexID))
ImGui.TreePop()
end
ImGui.PopID()
end
function ImGui.ShowFontAtlas(atlas)
local g = ImGui.GetCurrentContext()
local io = g.IO
local style = g.Style
ImGui.BeginDisabled()
ImGui.CheckboxFlags("io.BackendFlags: RendererHasTextures", io.BackendFlags, ImGuiBackendFlags.RendererHasTextures)
ImGui.EndDisabled()
ImGui.ShowFontSelector("Font")
local changed
style.FontSizeBase, changed = ImGui.DragFloat("FontSizeBase", style.FontSizeBase, 0.20, 5.0, 100.0, "%.0f")
if changed then style._NextFrameFontSizeBase = style.FontSizeBase end
ImGui.SameLine(0.0, 0.0); ImGui.Text(" (out %.2f)", ImGui.GetFontSize())
ImGui.SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later.")
style.FontScaleMain = ImGui.DragFloat("FontScaleMain", style.FontScaleMain, 0.02, 0.5, 4.0)
style.FontScaleDpi = ImGui.DragFloat("FontScaleDpi", style.FontScaleDpi, 0.02, 0.5, 4.0)
ImGui.BulletText("Load a nice font for better results!")
ImGui.BulletText("Please submit feedback:")
ImGui.SameLine(); ImGui.TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465")
ImGui.BulletText("Read FAQ for more details:")
ImGui.SameLine(); ImGui.TextLinkOpenURL("dearimgui.com/faq", "https://www.dearimgui.com/faq/")
ImGui.SeparatorText("Font List")
local cfg = g.DebugMetricsConfig
_, cfg.ShowFontPreview = ImGui.Checkbox("Show font preview", cfg.ShowFontPreview == true)
if ImGui.TreeNode("Loader", "Loader: '%s'", atlas.FontLoaderName or "NULL") then
ImGui.BeginDisabled()
ImGui.RadioButtonEx("stb_truetype", true)
ImGui.SameLine()
ImGui.RadioButtonEx("FreeType", false)
ImGui.SetItemTooltip("Not available on Roblox.")
ImGui.EndDisabled()
ImGui.TreePop()
end
for i, font in atlas.Fonts:iter() do
ImGui.PushID(i)
ImGui.DebugNodeFont(font)
ImGui.PopID()
end
ImGui.SeparatorText("Font Atlas")
if ImGui.Button("Compact") then atlas:CompactCache() end
ImGui.SameLine()
if ImGui.Button("Grow") then ImFontAtlasTextureGrow(atlas) end
ImGui.SameLine()
if ImGui.Button("Clear All") then ImFontAtlasBuildClear(atlas) end
ImGui.SetItemTooltip("Destroy cache and custom rectangles.")
for tex_n, tex in atlas.TexList:iter() do
if tex_n > 1 then ImGui.SameLine() end
ImGui.Text("Tex: %dx%d", tex.Width, tex.Height)
end
local b = atlas.Builder
if b then
local packed_sqrt = math.floor(math.sqrt(b.RectsPackedSurface or 0))
local discarded_sqrt = math.floor(math.sqrt(b.RectsDiscardedSurface or 0))
ImGui.Text("Packed rects: %d, area: about %d px ~%dx%d px", b.RectsPackedCount or 0, b.RectsPackedSurface or 0, packed_sqrt, packed_sqrt)
ImGui.Text("incl. Discarded rects: %d, area: about %d px ~%dx%d px", b.RectsDiscardedCount or 0, b.RectsDiscardedSurface or 0, discarded_sqrt, discarded_sqrt)
end
for tex_n, tex in atlas.TexList:iter() do
if tex_n == atlas.TexList.Size then ImGui.SetNextItemOpen(true, ImGuiCond.Once) end
ImGui.DebugNodeTexture(tex, atlas.TexList.Size - tex_n, nil)
end
end
----------------------------------------------------------------
-- [SECTION] Debug Log window
----------------------------------------------------------------
local function SameLineOrWrap(size)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
local pos = ImVec2(window.DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window.DC.CursorPosPrevLine.y)
if window.WorkRect:Contains(ImRect(pos, pos + size)) then ImGui.SameLine() end
end
local function ShowDebugLogFlag(name, flags)
local g = ImGui.GetCurrentContext()
local size = ImVec2(ImGui.GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui.CalcTextSize(name).x, ImGui.GetFrameHeight())
SameLineOrWrap(size)
local pressed
pressed, g.DebugLogFlags = ImGui.CheckboxFlags(name, g.DebugLogFlags, flags)
if pressed and g.IO.KeyShift and bit32.band(g.DebugLogFlags, flags) ~= 0 then
g.DebugLogAutoDisableFrames = 2
g.DebugLogAutoDisableFlags = bit32.bor(g.DebugLogAutoDisableFlags or 0, flags)
end
ImGui.SetItemTooltip("Hold Shift when clicking to enable for 2 frames only (useful for spammy log entries)")
end
function ImGui.ShowDebugLogWindow(p_open)
local g = ImGui.GetCurrentContext()
if bit32.band(g.NextWindowData.HasFlags, ImGuiNextWindowDataFlags.HasSize) == 0 then
ImGui.SetNextWindowSize(ImVec2(0.0, ImGui.GetFontSize() * 12.0), ImGuiCond.FirstUseEver)
end
local visible
p_open, visible = ImGui.Begin("ImGui Sincerely Debug Log", p_open)
if not visible or ImGui.GetCurrentWindow().BeginCount > 1 then
ImGui.End()
return p_open
end
local L = ImGuiDebugLogFlags
local all_enable_flags = bit32.band(L.EventMask_, bit32.bnot(L.EventInputRouting))
_, g.DebugLogFlags = ImGui.CheckboxFlags("All", g.DebugLogFlags, all_enable_flags)
ImGui.SetItemTooltip("(except InputRouting which is spammy)")
for _, e in ipairs({ { "Errors", L.EventError }, { "ActiveId", L.EventActiveId }, { "Clipper", L.EventClipper }, { "Docking", L.EventDocking },
{ "Focus", L.EventFocus }, { "IO", L.EventIO }, { "Font", L.EventFont }, { "Nav", L.EventNav }, { "Popup", L.EventPopup },
{ "Selection", L.EventSelection }, { "Table", L.EventTable }, { "Viewport", L.EventViewport }, { "InputRouting", L.EventInputRouting } }) do
ShowDebugLogFlag(e[1], e[2])
end
if ImGui.SmallButton("Clear") then
if g.DebugLogBuf then g.DebugLogBuf:clear() end
g.DebugLogLines = { "" }
end
ImGui.SameLine()
if ImGui.SmallButton("Copy") then ImGui.SetClipboardText(g.DebugLogBuf and g.DebugLogBuf:c_str() or "") end
ImGui.SameLine()
if ImGui.SmallButton("Configure Outputs..") then ImGui.OpenPopup("Outputs") end
if ImGui.BeginPopup("Outputs") then
_, g.DebugLogFlags = ImGui.CheckboxFlags("OutputToTTY", g.DebugLogFlags, L.OutputToTTY)
ImGui.BeginDisabled()
_, g.DebugLogFlags = ImGui.CheckboxFlags("OutputToDebugger", g.DebugLogFlags, L.OutputToDebugger)
_, g.DebugLogFlags = ImGui.CheckboxFlags("OutputToTestEngine", g.DebugLogFlags, L.OutputToTestEngine)
ImGui.EndDisabled()
ImGui.EndPopup()
end
ImGui.BeginChild("##log", ImVec2(0.0, 0.0), ImGuiChildFlags.Borders, bit32.bor(ImGuiWindowFlags.AlwaysVerticalScrollbar, ImGuiWindowFlags.AlwaysHorizontalScrollbar))
local backup_log_flags = g.DebugLogFlags
g.DebugLogFlags = bit32.band(g.DebugLogFlags, bit32.bnot(L.EventClipper))
local lines = g.DebugLogLines or { "" }
local count = #lines
if lines[count] == "" then count = count - 1 end -- trailing partial line
local clipper = ImGuiListClipper()
clipper:Begin(count)
while clipper:Step() do
for line_no = clipper.DisplayStart, clipper.DisplayEnd - 1 do ImGui.TextUnformatted(lines[line_no + 1]) end
end
g.DebugLogFlags = backup_log_flags
if ImGui.GetScrollY() >= ImGui.GetScrollMaxY() then ImGui.SetScrollHereY(1.0) end
ImGui.EndChild()
ImGui.End()
return p_open
end
----------------------------------------------------------------
-- [SECTION] ID Stack Tool
----------------------------------------------------------------
local function PathQuery(g)
local q = g.DebugItemPathQuery
if q == nil then
q = { MainID = 0, Active = false, Complete = false, Step = -1, Results = {} }
g.DebugItemPathQuery = q
g.DebugIDStackTool = { LastActiveFrame = -1, OptHexEncodeNonAsciiChars = true, OptCopyToClipboardOnCtrlC = false, CopyToClipboardLastTime = -FLT_MAX }
end
return q
end
-- Update queries. The steps are: -1: query Stack, >= 0: query each stack item
local function DebugItemPathQuery_UpdateAndGetHookId(query, id)
if query.MainID ~= id then
query.MainID = id
query.Step = -1
query.Complete = false
query.Results = {}
end
query.Active = false
if id == 0 then return 0 end
local res = query.Results
if query.Step >= 0 and query.Step < #res then
if res[query.Step + 1].QuerySuccess or res[query.Step + 1].QueryFrameCount > 2 then query.Step = query.Step + 1 end
end
query.Complete = (query.Step == #res)
if query.Step == -1 then
query.Active = true
return id
elseif query.Step >= 0 and query.Step < #res then
res[query.Step + 1].QueryFrameCount = res[query.Step + 1].QueryFrameCount + 1
query.Active = true
return res[query.Step + 1].ID
end
return 0
end
function ImGui.UpdateDebugToolItemPathQuery()
local g = ImGui.GetCurrentContext()
local q = PathQuery(g)
local id = 0
if g.DebugIDStackTool.LastActiveFrame + 1 == g.FrameCount then
id = (g.HoveredIdPreviousFrame ~= 0 and g.HoveredIdPreviousFrame) or g.ActiveId
end
g.DebugHookIdInfoId = DebugItemPathQuery_UpdateAndGetHookId(q, id)
end
-- Hooks called by GetID() family functions. `data_id` is the string/number that was hashed.
function ImGui.DebugHookIdInfo(id, data_type, data_id)
local g = ImGui.GetCurrentContext()
local query = PathQuery(g)
if not query.Active then return end
local window = g.CurrentWindow
if query.Step == -1 then
query.Step = 0
query.Results = {}
for n = 0, window.IDStack.Size do
query.Results[n + 1] = { ID = (n < window.IDStack.Size) and window.IDStack.Data[n + 1] or id, QueryFrameCount = 0, QuerySuccess = false, DataType = -1, Desc = nil }
end
return
end
if query.Step ~= window.IDStack.Size then return end
local info = query.Results[query.Step + 1]
if info == nil or info.ID ~= id then return end
if info.Desc == nil then
if data_type == ImGuiDataType.S32 then info.Desc = string.format("%d", data_id)
elseif data_type == ImGuiDataType.String then info.Desc = tostring(data_id)
else info.Desc = string.format("0x%08X [override]", id) end
end
info.QuerySuccess = true
if info.DataType == -1 then info.DataType = data_type end
end
local function SkipUncontributingPrefix(label)
local p = string.find(label, "###", 1, true)
if p then return string.sub(label, p) end
return label
end
local function FormatLevelInfo(g, query, n, format_for_ui)
local info = query.Results[n + 1]
local window = (info.Desc == nil and n == 0) and ImGui.FindWindowByID(info.ID) or nil
if window then return string.format(format_for_ui and "\"%s\" [window]" or "%s", SkipUncontributingPrefix(window.Name)) end
if info.QuerySuccess then return string.format((format_for_ui and info.DataType == ImGuiDataType.String) and "\"%s\"" or "%s", SkipUncontributingPrefix(info.Desc)) end
if query.Step < #query.Results then return "" end
return "???"
end
local function GetResultAsPath(g, query, hex_encode_non_ascii_chars)
local out = {}
for n = 0, #query.Results - 1 do
local desc = FormatLevelInfo(g, query, n, false)
out[#out + 1] = (n == 0) and "//" or "/"
desc = string.gsub(desc, "/", "\\/")
if hex_encode_non_ascii_chars then
desc = string.gsub(desc, "[\128-\255]", function(c) return string.format("\\x%02x", string.byte(c)) end)
end
out[#out + 1] = desc
end
return table.concat(out)
end
function ImGui.ShowIDStackToolWindow(p_open)
local g = ImGui.GetCurrentContext()
if bit32.band(g.NextWindowData.HasFlags, ImGuiNextWindowDataFlags.HasSize) == 0 then
ImGui.SetNextWindowSize(ImVec2(0.0, ImGui.GetFontSize() * 8.0), ImGuiCond.FirstUseEver)
end
local visible
p_open, visible = ImGui.Begin("ImGui Sincerely ID Stack Tool", p_open)
if not visible or ImGui.GetCurrentWindow().BeginCount > 1 then
ImGui.End()
return p_open
end
local query = PathQuery(g)
local tool = g.DebugIDStackTool
tool.LastActiveFrame = g.FrameCount
local result_path = GetResultAsPath(g, query, tool.OptHexEncodeNonAsciiChars)
ImGui.Text("0x%08X", query.MainID)
ImGui.SameLine()
MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.")
local time_since_copy = g.Time - tool.CopyToClipboardLastTime
ImGui.PushStyleVarY(ImGuiStyleVar.FramePadding, 0.0)
_, tool.OptHexEncodeNonAsciiChars = ImGui.Checkbox("Hex-encode non-ASCII", tool.OptHexEncodeNonAsciiChars)
ImGui.SameLine()
_, tool.OptCopyToClipboardOnCtrlC = ImGui.Checkbox("Ctrl+C: copy path", tool.OptCopyToClipboardOnCtrlC)
ImGui.PopStyleVar()
ImGui.SameLine()
ImGui.TextColored((time_since_copy >= 0.0 and time_since_copy < 0.75 and math.fmod(time_since_copy, 0.25) < 0.25 * 0.5) and ImVec4(1.0, 1.0, 0.3, 1.0) or ImVec4(), "*COPIED*")
if tool.OptCopyToClipboardOnCtrlC and ImGui.Shortcut(bit32.bor(ImGuiMod_Ctrl, ImGuiKey.C), bit32.bor(ImGuiInputFlags.RouteGlobal, ImGuiInputFlags.RouteOverFocused)) then
tool.CopyToClipboardLastTime = g.Time
ImGui.SetClipboardText(result_path)
end
ImGui.Text("- Path \"%s\"", query.Complete and result_path or "")
ImGui.Separator()
if #query.Results > 0 and ImGui.BeginTable("##table", 3, ImGuiTableFlags.Borders) then
local id_width = ImGui.CalcTextSize("0xDDDDDDDD").x
ImGui.TableSetupColumn("Seed", ImGuiTableColumnFlags.WidthFixed, id_width)
ImGui.TableSetupColumn("PushID", ImGuiTableColumnFlags.WidthStretch)
ImGui.TableSetupColumn("Result", ImGuiTableColumnFlags.WidthFixed, id_width)
ImGui.TableHeadersRow()
for n = 0, #query.Results - 1 do
local info = query.Results[n + 1]
ImGui.TableNextColumn()
ImGui.Text("0x%08X", (n > 0) and query.Results[n].ID or 0)
ImGui.TableNextColumn()
ImGui.TextUnformatted(FormatLevelInfo(g, query, n, true))
ImGui.TableNextColumn()
ImGui.Text("0x%08X", info.ID)
if n == #query.Results - 1 then ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, ImGui.GetColorU32(ImGuiCol.Header)) end
end
ImGui.EndTable()
end
ImGui.End()
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 "<NULL>")
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: <INVALID>") 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