|
|
|
@@ -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
|