tools: About, Debug Log, ID Stack Tool, ShowFontAtlas/DebugNodeFont/Texture, ShowFontSelector; debug log macros route to DebugLog; GetID hooks

This commit is contained in:
4DBug
2026-10-08 08:33:57 -05:00
parent 1862183f78
commit 91d8a2847f
7 changed files with 1288 additions and 25 deletions
+31 -6
View File
@@ -2555,7 +2555,9 @@ end
function ImGui.PushOverrideID(id)
local g = GImGui
local window = g.CurrentWindow
-- TODO: DebugHookIdInfo()
if g.DebugHookIdInfoId == id and id ~= 0 then
ImGui.DebugHookIdInfo(id, ImGuiDataType.ID, nil)
end
window.IDStack:push_back(id)
end
@@ -2570,11 +2572,17 @@ end
function MT.ImGuiWindow:GetID(id)
local seed = self.IDStack:back()
local h
if type(id) == "string" then
return ImHashStr(id, nil, seed)
h = ImHashStr(id, nil, seed)
else --- @cast id int
return ImHashData(id, -1, seed)
h = ImHashData(id, -1, seed)
end
local g = GImGui
if g.DebugHookIdInfoId == h and h ~= 0 then
ImGui.DebugHookIdInfo(h, (type(id) == "string") and ImGuiDataType.String or ImGuiDataType.S32, id)
end
return h
end
-- This is only used in rare/specific situations to manufacture an ID out of nowhere
@@ -2610,10 +2618,16 @@ end
-- Overloads: GetIDWithSeed(str, str_end, seed) / GetIDWithSeed(n, seed)
function ImGui.GetIDWithSeed(a, b, c)
local id
if type(a) == "number" then
return ImHashData(a, -1, b)
id = ImHashData(a, -1, b)
else
id = ImHashStr(a, b and (b - 1) or nil, c)
end
return ImHashStr(a, b and (b - 1) or nil, c)
if GImGui.DebugHookIdInfoId == id and id ~= 0 then
ImGui.DebugHookIdInfo(id, (type(a) == "number") and ImGuiDataType.S32 or ImGuiDataType.String, (b and type(a) == "string") and string.sub(a, 1, b - 1) or a)
end
return id
end
--- @param id ImGuiID
@@ -8292,6 +8306,9 @@ function ImGui.NewFrame()
g.CurrentItemFlags = g.ItemFlagsStack:back()
g.GroupStack:resize(0)
-- [DEBUG] ID Stack Tool query
ImGui.UpdateDebugToolItemPathQuery()
-- Docking
ImGui.DockContextNewFrameUpdateDocking(g)
@@ -13637,8 +13654,16 @@ function ImGui.DebugLog(fmt, ...)
g.DebugLogBuf = g.DebugLogBuf or ImGuiTextBuffer()
local str = string.format("[%05d] ", g.FrameCount) .. ((select("#", ...) > 0) and string.format(fmt, ...) or fmt)
g.DebugLogBuf:append(str)
-- Line index (upstream: g.DebugLogIndex). Lines are complete strings without '\n'.
local lines = g.DebugLogLines
if lines == nil then lines = { "" }; g.DebugLogLines = lines end
for part, nl in string.gmatch(str, "([^\n]*)(\n?)") do
lines[#lines] = lines[#lines] .. part
if nl ~= "" then lines[#lines + 1] = "" end
if part == "" and nl == "" then break end
end
if bit32.band(g.DebugLogFlags, ImGuiDebugLogFlags.OutputToTTY) ~= 0 then
print(str)
print((string.gsub(str, "\n$", "")))
end
end
+578
View File
@@ -0,0 +1,578 @@
-- 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 = ImFontAtlasPackGetRect(font.OwnerAtlas, glyph.PackId)
if 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
return true
+7 -2
View File
@@ -1414,7 +1414,7 @@ function ImFontAtlasBakedDiscard(atlas, font, baked)
font.LastBaked = nil
end
local function ImFontAtlasFontDiscardBakes(atlas, font, unused_frames)
function ImFontAtlasFontDiscardBakes(atlas, font, unused_frames)
local builder = atlas.Builder
if builder then
for baked_n = 1, builder.BakedPool.Size do
@@ -1746,7 +1746,7 @@ function ImFontAtlasTextureGetSizeEstimate(atlas)
end
--- @param atlas ImFontAtlas
local function ImFontAtlasBuildClear(atlas)
function ImFontAtlasBuildClear(atlas)
local new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas) -- ImVec2i
ImFontAtlasBuildDestroy(atlas)
ImFontAtlasTextureAdd(atlas, new_tex_size.x, new_tex_size.y)
@@ -2098,6 +2098,11 @@ function MT.ImFontBaked:FindGlyph(c)
return (glyph) and glyph or self.Glyphs.Data[self.FallbackGlyphIndex]
end
function MT.ImFontBaked:IsGlyphLoaded(c)
if c >= self.IndexLookup.Size then return false end
return self.IndexLookup.Data[c + 1] ~= IM_FONTGLYPH_INDEX_UNUSED
end
--- @param c ImWchar
--- @return ImFontGlyph?
function MT.ImFontBaked:FindGlyphNoFallback(c)
+2
View File
@@ -111,6 +111,8 @@ ImGuiDataType =
String = 9, -- string (provided for user convenience, not supported by scalar widgets)
COUNT = 10
}
ImGuiDataType.Pointer = 11 -- [internal]
ImGuiDataType.ID = 12 -- [internal]
ImTextureID_Invalid = -1
+18 -4
View File
@@ -439,10 +439,23 @@ IM_DRAWLIST_ARCFAST_SAMPLE_MAX = IM_DRAWLIST_ARCFAST_TABLE_SIZE
function IM_ASSERT_USER_ERROR(_EXPR, _MSG) if not (_EXPR) or (_EXPR) == 0 then if not ImGui.ErrorLog or ImGui.ErrorLog(_MSG) then error(_MSG, 2) end end end
function IM_ASSERT_USER_ERROR_RET(_EXPR, _MSG) if not (_EXPR) or (_EXPR) == 0 then error(_MSG, 2) end end
function IMGUI_DEBUG_LOG_ACTIVEID(_str, ...) local g = GImGui if bitAnd(g.DebugLogFlags, ImGuiDebugLogFlags.EventActiveId) ~= 0 then print(string.format(_str, ...)) end end
function IMGUI_DEBUG_LOG_POPUP(_str, ...) local g = GImGui if bitAnd(g.DebugLogFlags, ImGuiDebugLogFlags.EventPopup) ~= 0 then print(string.format(_str, ...)) end end
function IMGUI_DEBUG_LOG_FONT(_str, ...) local g2 = GImGui if g2 and bitAnd(g2.DebugLogFlags, ImGuiDebugLogFlags.EventFont) ~= 0 then print(string.format(_str, ...)) end end
function IMGUI_DEBUG_LOG_VIEWPORT(_str, ...) local g = GImGui if bitAnd(g.DebugLogFlags, ImGuiDebugLogFlags.EventViewport) ~= 0 then print(string.format(_str, ...)) end end
local function DebugLogIf(flag, fmt, ...)
local g = GImGui
if g and bitAnd(g.DebugLogFlags, flag) ~= 0 then ImGui.DebugLog(fmt, ...) end
end
function IMGUI_DEBUG_LOG_ACTIVEID(...) DebugLogIf(ImGuiDebugLogFlags.EventActiveId, ...) end
function IMGUI_DEBUG_LOG_POPUP(...) DebugLogIf(ImGuiDebugLogFlags.EventPopup, ...) end
function IMGUI_DEBUG_LOG_FONT(...) DebugLogIf(ImGuiDebugLogFlags.EventFont, ...) end
function IMGUI_DEBUG_LOG_VIEWPORT(...) DebugLogIf(ImGuiDebugLogFlags.EventViewport, ...) end
function IMGUI_DEBUG_LOG_FOCUS(...) DebugLogIf(ImGuiDebugLogFlags.EventFocus, ...) end
function IMGUI_DEBUG_LOG_NAV(...) DebugLogIf(ImGuiDebugLogFlags.EventNav, ...) end
function IMGUI_DEBUG_LOG_IO(...) DebugLogIf(ImGuiDebugLogFlags.EventIO, ...) end
function IMGUI_DEBUG_LOG_INPUTROUTING(...) DebugLogIf(ImGuiDebugLogFlags.EventInputRouting, ...) end
function IMGUI_DEBUG_LOG_TABLE(...) DebugLogIf(ImGuiDebugLogFlags.EventTable, ...) end
function IMGUI_DEBUG_LOG_DOCKING(...) DebugLogIf(ImGuiDebugLogFlags.EventDocking, ...) end
function IMGUI_DEBUG_LOG_SELECTION(...) DebugLogIf(ImGuiDebugLogFlags.EventSelection, ...) end
function IMGUI_DEBUG_LOG_CLIPPER(...) DebugLogIf(ImGuiDebugLogFlags.EventClipper, ...) end
function IMGUI_DEBUG_LOG_ERROR(...) DebugLogIf(ImGuiDebugLogFlags.EventError, ...) end
--- @alias ImGuiSelectionUserData any
ImGuiSelectionUserData_Invalid = -1
@@ -2123,6 +2136,7 @@ function ImGuiContext(shared_font_atlas) -- TODO: tidy up / complete this struct
DebugFlashStyleColorIdx = nil,
DebugMetricsConfig = ImGuiMetricsConfig(),
DebugHookIdInfoId = 0,
DebugAllocInfo = ImGuiDebugAllocInfo(),
--- Misc