tables: full port of imgui_tables.cpp (layout, borders, draw channel merge, sorting, headers, angled headers, context menu, .ini settings, reconcile, GC) + legacy Columns API; demo: ShowUserGuide, DemoWindowColumns; misc widget aliases

This commit is contained in:
4DBug
2026-10-08 06:53:00 -05:00
parent 9860373ce5
commit 701d1d3449
8 changed files with 9260 additions and 14 deletions
+1 -1
View File
@@ -1 +1 @@
[[12, "imgui.lua"], [13676, "imgui_demo.lua"], [14569, "imgui_demo_2.lua"], [17457, "imgui_docking.lua"], [20585, "imgui_dragdrop.lua"], [20877, "imgui_draw.lua"], [25872, "imgui_h.lua"], [28026, "imgui_impl_roblox.lua"], [28791, "imgui_impl_roblox_fonts.lua"], [29569, "imgui_internal.lua"], [32724, "imgui_tables.lua"], [32771, "imgui_tabs.lua"], [33938, "imgui_widgets.lua"], [42397, "imstb_rectpack.lua"], [42843, "imstb_textedit.lua"], [44084, "imstb_truetype.lua"], [46552, "imstd_minstdio.lua"]]
[[12, "imgui.lua"], [13676, "imgui_demo.lua"], [14613, "imgui_demo_2.lua"], [17688, "imgui_docking.lua"], [20816, "imgui_dragdrop.lua"], [21108, "imgui_draw.lua"], [26103, "imgui_h.lua"], [28257, "imgui_impl_roblox.lua"], [29022, "imgui_impl_roblox_fonts.lua"], [29800, "imgui_internal.lua"], [32956, "imgui_tables.lua"], [37303, "imgui_tabs.lua"], [38470, "imgui_widgets.lua"], [46945, "imstb_rectpack.lua"], [47391, "imstb_textedit.lua"], [48632, "imstb_truetype.lua"], [51100, "imstd_minstdio.lua"]]
+4568 -7
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -887,4 +887,48 @@ function DemoWindowWidgets(data)
end
end
----------------------------------------------------------------
-- [SECTION] User Guide / ShowUserGuide()
----------------------------------------------------------------
function ImGui.ShowUserGuide()
local io = ImGui.GetIO()
ImGui.BulletText("Double-click on title bar to collapse window.")
ImGui.BulletText(
"Click and drag on lower corner or border to resize window.\n" ..
"(double-click to auto fit window to its contents)")
ImGui.BulletText("Ctrl+Click on a slider or drag box to input value as text.")
ImGui.BulletText("Tab/Shift+Tab to cycle through keyboard editable fields.")
ImGui.BulletText("Ctrl+Tab/Ctrl+Shift+Tab to focus windows.")
if io.FontAllowUserScaling then
ImGui.BulletText("Ctrl+Mouse Wheel to zoom window contents.")
end
ImGui.BulletText("While inputting text:\n")
ImGui.Indent()
ImGui.BulletText("Ctrl+Left/Right to word jump.")
ImGui.BulletText("Ctrl+A or double-click to select all.")
ImGui.BulletText("Ctrl+X/C/V to use clipboard cut/copy/paste.")
ImGui.BulletText("Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.")
ImGui.BulletText("Escape to revert.")
ImGui.Unindent()
ImGui.BulletText("With Keyboard controls enabled:")
ImGui.Indent()
ImGui.BulletText("Arrow keys or Home/End/PageUp/PageDown to navigate.")
ImGui.BulletText("Space to activate a widget.")
ImGui.BulletText("Return to input text into a widget.")
ImGui.BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus.")
ImGui.BulletText("Alt to jump to the menu layer of a window.")
ImGui.BulletText("Menu or Shift+F10 to open a context menu.")
ImGui.Unindent()
ImGui.BulletText("With Gamepad controls enabled:")
ImGui.Indent()
ImGui.BulletText("D-Pad: Navigate / Tweak / Resize (in Windowing mode).")
ImGui.BulletText("%s Face button: Activate / Open / Toggle. Hold: activate with text input.", io.ConfigNavSwapGamepadButtons and "East" or "South")
ImGui.BulletText("%s Face button: Cancel / Close / Exit.", io.ConfigNavSwapGamepadButtons and "South" or "East")
ImGui.BulletText("West Face button: Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows).")
ImGui.BulletText("North Face button: Open Context Menu.")
ImGui.BulletText("L1/R1: Tweak Slower/Faster, Focus Previous/Next (in Windowing Mode).")
ImGui.Unindent()
end
return true -- [Roblox] ModuleScripts must return exactly one value
+189 -1
View File
@@ -2804,6 +2804,195 @@ local function Tables_Advanced()
end
end
local DCS = { selected = -1, h_borders = true, v_borders = true, columns_count = 4, foo = 1.0, bar = 1.0 }
local function DemoWindowColumns()
local open = ImGui.TreeNode("Legacy Columns API")
ImGui.SameLine()
DemoHelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!")
if not open then
return
end
-- Basic columns
if ImGui.TreeNode("Basic") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Basic")
ImGui.Text("Without border:")
ImGui.Columns(3, "mycolumns3", false) -- 3-ways, no border
ImGui.Separator()
for n = 0, 13 do
local label = string.format("Item %d", n)
if ImGui.Selectable(label) then end
ImGui.NextColumn()
end
ImGui.Columns(1)
ImGui.Separator()
ImGui.Text("With border:")
ImGui.Columns(4, "mycolumns") -- 4-ways, with border
ImGui.Separator()
ImGui.Text("ID"); ImGui.NextColumn()
ImGui.Text("Name"); ImGui.NextColumn()
ImGui.Text("Path"); ImGui.NextColumn()
ImGui.Text("Hovered"); ImGui.NextColumn()
ImGui.Separator()
local names = { "One", "Two", "Three" }
local paths = { "/path/one", "/path/two", "/path/three" }
for i = 0, 2 do
local label = string.format("%04d", i)
if ImGui.Selectable(label, DCS.selected == i, ImGuiSelectableFlags.SpanAllColumns) then
DCS.selected = i
end
local hovered = ImGui.IsItemHovered()
ImGui.NextColumn()
ImGui.Text(names[i + 1]); ImGui.NextColumn()
ImGui.Text(paths[i + 1]); ImGui.NextColumn()
ImGui.Text("%d", hovered and 1 or 0); ImGui.NextColumn()
end
ImGui.Columns(1)
ImGui.Separator()
ImGui.TreePop()
end
if ImGui.TreeNode("Borders") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Borders")
-- NB: Future columns API should allow automatic horizontal borders.
local lines_count = 3
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
DCS.columns_count = ImGui.DragInt("##columns_count", DCS.columns_count, 0.1, 2, 10, "%d columns")
if DCS.columns_count < 2 then
DCS.columns_count = 2
end
ImGui.SameLine()
_, DCS.h_borders = ImGui.Checkbox("horizontal", DCS.h_borders)
ImGui.SameLine()
_, DCS.v_borders = ImGui.Checkbox("vertical", DCS.v_borders)
ImGui.Columns(DCS.columns_count, nil, DCS.v_borders)
for i = 0, DCS.columns_count * lines_count - 1 do
if DCS.h_borders and ImGui.GetColumnIndex() == 0 then
ImGui.Separator()
end
ImGui.PushID(i)
local c = string.char(string.byte("a") + i)
ImGui.Text("%s%s%s", c, c, c)
ImGui.Text("Width %.2f", ImGui.GetColumnWidth())
ImGui.Text("Avail %.2f", ImGui.GetContentRegionAvail().x)
ImGui.Text("Offset %.2f", ImGui.GetColumnOffset())
ImGui.Text("Long text that is likely to clip")
ImGui.Button("Button", ImVec2(-FLT_MIN, 0.0))
ImGui.PopID()
ImGui.NextColumn()
end
ImGui.Columns(1)
if DCS.h_borders then
ImGui.Separator()
end
ImGui.TreePop()
end
-- Create multiple items in a same cell before switching to next column
if ImGui.TreeNode("Mixed items") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items")
ImGui.Columns(3, "mixed")
ImGui.Separator()
ImGui.Text("Hello")
ImGui.Button("Banana")
ImGui.NextColumn()
ImGui.Text("ImGui")
ImGui.Button("Apple")
DCS.foo = ImGui.InputFloat("red", DCS.foo, 0.05, 0, "%.3f")
ImGui.Text("An extra line here.")
ImGui.NextColumn()
ImGui.Text("Sailor")
ImGui.Button("Corniflower")
DCS.bar = ImGui.InputFloat("blue", DCS.bar, 0.05, 0, "%.3f")
ImGui.NextColumn()
if ImGui.CollapsingHeader("Category A") then ImGui.Text("Blah blah blah") end ImGui.NextColumn()
if ImGui.CollapsingHeader("Category B") then ImGui.Text("Blah blah blah") end ImGui.NextColumn()
if ImGui.CollapsingHeader("Category C") then ImGui.Text("Blah blah blah") end ImGui.NextColumn()
ImGui.Columns(1)
ImGui.Separator()
ImGui.TreePop()
end
-- Word wrapping
if ImGui.TreeNode("Word-wrapping") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping")
ImGui.Columns(2, "word-wrapping")
ImGui.Separator()
ImGui.TextWrapped("The quick brown fox jumps over the lazy dog.")
ImGui.TextWrapped("Hello Left")
ImGui.NextColumn()
ImGui.TextWrapped("The quick brown fox jumps over the lazy dog.")
ImGui.TextWrapped("Hello Right")
ImGui.Columns(1)
ImGui.Separator()
ImGui.TreePop()
end
if ImGui.TreeNode("Horizontal Scrolling") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling")
ImGui.SetNextWindowContentSize(ImVec2(1500.0, 0.0))
local child_size = ImVec2(0, ImGui.GetFontSize() * 20.0)
ImGui.BeginChild("##ScrollingRegion", child_size, ImGuiChildFlags.None, ImGuiWindowFlags.HorizontalScrollbar)
ImGui.Columns(10)
-- Also demonstrate using clipper for large vertical lists
local ITEMS_COUNT = 2000
local clipper = ImGuiListClipper()
clipper:Begin(ITEMS_COUNT)
while clipper:Step() do
for i = clipper.DisplayStart, clipper.DisplayEnd - 1 do
for j = 0, 9 do
ImGui.Text("Line %d Column %d...", i, j)
ImGui.NextColumn()
end
end
end
ImGui.Columns(1)
ImGui.EndChild()
ImGui.TreePop()
end
if ImGui.TreeNode("Tree") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Tree")
ImGui.Columns(2, "tree", true)
for x = 0, 2 do
local open1 = ImGui.TreeNode(x, "Node%d", x)
ImGui.NextColumn()
ImGui.Text("Node contents")
ImGui.NextColumn()
if open1 then
for y = 0, 2 do
local open2 = ImGui.TreeNode(y, "Node%d.%d", x, y)
ImGui.NextColumn()
ImGui.Text("Node contents")
if open2 then
ImGui.Text("Even more contents")
if ImGui.TreeNode("Tree in column") then
ImGui.Text("The quick brown fox jumps over the lazy dog")
ImGui.TreePop()
end
end
ImGui.NextColumn()
if open2 then
ImGui.TreePop()
end
end
ImGui.TreePop()
end
end
ImGui.Columns(1)
ImGui.TreePop()
end
ImGui.TreePop()
end
local tables_sections = {
{ "Basic", Tables_Basic },
{ "Borders, background", Tables_BordersBackground },
@@ -2882,4 +3071,3 @@ function DemoWindowTables()
end
end
--@@COLUMNS@@
+1
View File
@@ -2273,6 +2273,7 @@ local function ImGuiWindowTempData()
IsSameLine = false,
IsSetPos = false,
NavLayersActiveMask = 0, NavLayersActiveMaskNext = 0,
Indent = ImVec1(),
ColumnsOffset = ImVec1(),
+4305 -5
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -6807,6 +6807,22 @@ end
-- [SECTION] TREES
----------------------------------------------------------------
-- va_list variants: identical in Lua (varargs)
ImGui.TextColoredV = function(...) return ImGui.TextColored(...) end
ImGui.TextDisabledV = function(...) return ImGui.TextDisabled(...) end
ImGui.TextWrappedV = function(...) return ImGui.TextWrapped(...) end
ImGui.TextAlignedV = function(...) return ImGui.TextAligned(...) end
ImGui.LabelTextV = function(...) return ImGui.LabelText(...) end
ImGui.BulletTextV = function(...) return ImGui.BulletText(...) end
--- @param data_type ImGuiDataType
--- @param v number
function ImGui.SetNextItemRefVal(data_type, v)
local g = GImGui
g.NextItemData.HasFlags = bit32.bor(g.NextItemData.HasFlags, ImGuiNextItemDataFlags.HasRefVal)
g.NextItemData.RefVal = v
end
-- Overloads: TreeNode(label) / TreeNode(str_id, fmt, ...)
--- @param label string
function ImGui.TreeNode(label, fmt, ...)
+136
View File
@@ -0,0 +1,136 @@
-- Tables: basic, borders/rowbg, resizable+reorderable+hideable+sortable headers, scroll freeze + clipper, angled headers, legacy Columns, ini round trip
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local G = require(ReplicatedStorage:WaitForChild("ImGui"))
local ImGui, ImVec2 = G.ImGui, G.ImVec2
local TF, CF = G.ImGuiTableFlags, G.ImGuiTableColumnFlags
local bor = bit32.bor
ImGui.CreateContext()
G.ImGui_ImplRoblox.Init()
local frame = 0
local sorted_specs = nil
local visible_rows = 0
local hovered_col = nil
RunService.Stepped:Connect(function()
frame += 1
G.ImGui_ImplRoblox.NewFrame()
local io = ImGui.GetIO()
-- drag the border between column 0 and 1 of "sortable" on frames 10..20
ImGui.NewFrame()
ImGui.SetNextWindowPos(ImVec2(10, 10), G.ImGuiCond.Always)
ImGui.SetNextWindowSize(ImVec2(700, 900), G.ImGuiCond.Always)
ImGui.Begin("Tables")
if ImGui.BeginTable("basic", 3) then
for row = 0, 3 do
ImGui.TableNextRow()
for col = 0, 2 do
ImGui.TableSetColumnIndex(col)
ImGui.Text("Row %d Column %d", row, col)
end
end
ImGui.EndTable()
end
if ImGui.BeginTable("borders", 3, bor(TF.Borders, TF.RowBg)) then
for row = 0, 4 do
ImGui.TableNextRow()
for col = 0, 2 do
ImGui.TableNextColumn()
ImGui.Text("Hello %d,%d", col, row)
if row == 2 and col == 1 then ImGui.TableSetBgColor(G.ImGuiTableBgTarget.CellBg, G.IM_COL32(255, 0, 0, 255)) end
end
end
ImGui.EndTable()
end
if ImGui.BeginTable("sortable", 4, bor(TF.Resizable, TF.Reorderable, TF.Hideable, TF.Sortable, TF.SortMulti, TF.BordersOuter, TF.BordersV)) then
ImGui.TableSetupColumn("ID", bor(CF.DefaultSort, CF.WidthFixed), 0.0, 0)
ImGui.TableSetupColumn("Name", CF.WidthFixed, 0.0, 1)
ImGui.TableSetupColumn("Action", bor(CF.NoSort, CF.WidthFixed), 0.0, 2)
ImGui.TableSetupColumn("Quantity", bor(CF.PreferSortDescending, CF.WidthStretch), 0.0, 3)
ImGui.TableHeadersRow()
local specs = ImGui.TableGetSortSpecs()
if specs and specs.SpecsDirty then sorted_specs = specs.SpecsCount; specs.SpecsDirty = false end
for row = 0, 5 do
ImGui.TableNextRow()
ImGui.TableNextColumn(); ImGui.Text("%04d", row)
ImGui.TableNextColumn(); ImGui.TextUnformatted("Item " .. row)
ImGui.TableNextColumn(); ImGui.SmallButton("None##" .. row)
ImGui.TableNextColumn(); ImGui.Text("%d", row * 3)
end
hovered_col = ImGui.TableGetHoveredColumn()
ImGui.EndTable()
end
if ImGui.BeginTable("scroll", 3, bor(TF.ScrollY, TF.ScrollX, TF.RowBg, TF.BordersOuter, TF.BordersV, TF.Resizable, TF.SizingFixedFit), ImVec2(0, 200)) then
ImGui.TableSetupScrollFreeze(1, 1)
ImGui.TableSetupColumn("One", CF.None, 100)
ImGui.TableSetupColumn("Two", CF.None, 200)
ImGui.TableSetupColumn("Three", CF.None, 300)
ImGui.TableHeadersRow()
local clipper = G.ImGuiListClipper()
clipper:Begin(1000)
visible_rows = 0
while clipper:Step() do
for row = clipper.DisplayStart, clipper.DisplayEnd - 1 do
visible_rows += 1
ImGui.TableNextRow()
for column = 0, 2 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Hello %d,%d", column, row)
end
end
end
if frame == 4 then ImGui.SetScrollY(500) end
ImGui.EndTable()
end
if ImGui.BeginTable("angled", 4, bor(TF.Borders, TF.SizingFixedFit, TF.ScrollX), ImVec2(0, 150)) then
ImGui.TableSetupColumn("Name", CF.NoHide)
for i = 1, 3 do ImGui.TableSetupColumn("Angled " .. i, bor(CF.AngledHeader, CF.WidthFixed)) end
ImGui.TableAngledHeadersRow()
ImGui.TableHeadersRow()
for row = 0, 3 do
ImGui.TableNextRow()
ImGui.TableNextColumn(); ImGui.Text("Track %d", row)
for i = 1, 3 do ImGui.TableNextColumn(); ImGui.Checkbox("##c" .. row .. i, false) end
end
ImGui.EndTable()
end
-- nested + tree in table
if ImGui.BeginTable("tree", 2, bor(TF.BordersV, TF.Resizable, TF.RowBg)) then
ImGui.TableSetupColumn("Name", CF.NoHide)
ImGui.TableSetupColumn("Size", CF.WidthFixed, 80)
ImGui.TableHeadersRow()
ImGui.TableNextRow(); ImGui.TableNextColumn()
if ImGui.TreeNodeEx("Root", bor(G.ImGuiTreeNodeFlags.SpanAllColumns, G.ImGuiTreeNodeFlags.DefaultOpen)) then
ImGui.TableNextColumn(); ImGui.TextDisabled("--")
ImGui.TableNextRow(); ImGui.TableNextColumn()
ImGui.TreeNodeEx("Leaf", bor(G.ImGuiTreeNodeFlags.Leaf, G.ImGuiTreeNodeFlags.NoTreePushOnOpen, G.ImGuiTreeNodeFlags.SpanAllColumns))
ImGui.TableNextColumn(); ImGui.Text("123")
ImGui.TreePop()
end
ImGui.EndTable()
end
ImGui.Columns(3, "legacy")
for i = 0, 5 do ImGui.Text("col item %d", i); ImGui.NextColumn() end
ImGui.Columns(1)
ImGui.End()
ImGui.Render()
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
if frame == 8 then
assert(sorted_specs == 1, "sort specs " .. tostring(sorted_specs))
assert(visible_rows > 3 and visible_rows < 30, "clipped rows " .. visible_rows)
local ini = ImGui.SaveIniSettingsToMemory()
assert(string.find(ini, "[Table][", 1, true), "table ini missing")
print(ini)
ImGui.LoadIniSettingsFromMemory(ini)
assert(ImGui.SaveIniSettingsToMemory() == ini, "ini round trip")
print("visible rows", visible_rows, "TABLES OK")
end
end)