Initial commit

This commit is contained in:
4DBug
2026-10-07 13:03:05 -05:00
commit c2cf349390
17 changed files with 34877 additions and 0 deletions
+524
View File
@@ -0,0 +1,524 @@
--- ImGui Backend for Love2D
--- Structurally mirrors imgui_impl_gmod.lua
local love = love
local ImGui_ImplLOVE_GetBackendData
local ImGui_ImplLOVE_UpdateTexture
local ImGui_ImplLOVE_RenderDrawData
local ImGui_ImplLOVE_Shutdown
local CURSOR_MAP = {
[ImGuiMouseCursor.None] = nil,
[ImGuiMouseCursor.Arrow] = "arrow",
[ImGuiMouseCursor.TextInput] = "ibeam",
[ImGuiMouseCursor.ResizeAll] = "sizeall",
[ImGuiMouseCursor.ResizeNS] = "sizens",
[ImGuiMouseCursor.ResizeEW] = "sizewe",
[ImGuiMouseCursor.ResizeNESW] = "sizenesw",
[ImGuiMouseCursor.ResizeNWSE] = "sizenwse",
[ImGuiMouseCursor.Hand] = "hand",
[ImGuiMouseCursor.Wait] = "wait",
[ImGuiMouseCursor.Progress] = "waitarrow",
[ImGuiMouseCursor.NotAllowed] = "no",
}
local KEY_MAP = {
["return"] = ImGuiKey.Enter, ["escape"] = ImGuiKey.Escape,
["backspace"] = ImGuiKey.Backspace, ["tab"] = ImGuiKey.Tab,
["space"] = ImGuiKey.Space, ["enter"] = ImGuiKey.KeypadEnter,
["up"] = ImGuiKey.UpArrow, ["down"] = ImGuiKey.DownArrow,
["left"] = ImGuiKey.LeftArrow, ["right"] = ImGuiKey.RightArrow,
["home"] = ImGuiKey.Home, ["end"] = ImGuiKey.End,
["pageup"] = ImGuiKey.PageUp, ["pagedown"] = ImGuiKey.PageDown,
["insert"] = ImGuiKey.Insert, ["delete"] = ImGuiKey.Delete,
["lctrl"] = ImGuiKey.LeftCtrl, ["rctrl"] = ImGuiKey.RightCtrl,
["lshift"] = ImGuiKey.LeftShift, ["rshift"] = ImGuiKey.RightShift,
["lalt"] = ImGuiKey.LeftAlt, ["ralt"] = ImGuiKey.RightAlt,
["lgui"] = ImGuiKey.LeftSuper, ["rgui"] = ImGuiKey.RightSuper,
}
for i = 0, 9 do KEY_MAP[tostring(i)] = ImGuiKey["K" .. i] end
for i = 1, 12 do KEY_MAP["f" .. i] = ImGuiKey["F" .. i] end
for c in ("abcdefghijklmnopqrstuvwxyz"):gmatch(".") do
KEY_MAP[c] = ImGuiKey[c:upper()]
end
local function ImGui_ImplLOVE_UpdateKeyModifiers()
local io = ImGui.GetIO()
io:AddKeyEvent(ImGuiMod_Ctrl, love.keyboard.isDown("lctrl", "rctrl"))
io:AddKeyEvent(ImGuiMod_Shift, love.keyboard.isDown("lshift", "rshift"))
io:AddKeyEvent(ImGuiMod_Alt, love.keyboard.isDown("lalt", "ralt"))
io:AddKeyEvent(ImGuiMod_Super, love.keyboard.isDown("lgui", "rgui"))
end
--- @return table?
function ImGui_ImplLOVE_GetBackendData()
return ImGui.GetCurrentContext() and ImGui.GetIO().BackendPlatformUserData or nil
end
---------------------------------------------------------
-- VIEWPORT STUBS
-- Required for ImGui BackendPlatformHasViewports logic
---------------------------------------------------------
local function ImGui_ImplLOVE_CreateWindow(viewport)
-- STUB: Triggered when ImGui creates a new multi-viewport window (e.g., dragging a panel outside main window).
-- If your LÖVE fork supports multiple OS windows (or if managing internal canvases), initialize it here.
local vd = { WindowID = nil, Owned = true }
viewport.PlatformUserData = vd
viewport.PlatformHandle = vd
viewport.PlatformHandleRaw = vd
end
local function ImGui_ImplLOVE_DestroyWindow(viewport)
-- STUB: Destroy the OS-level window or release the canvas associated with this viewport.
viewport.PlatformUserData = nil
viewport.PlatformHandle = nil
end
local function ImGui_ImplLOVE_ShowWindow(viewport)
-- STUB: OS-level call to make the window visible.
end
local function ImGui_ImplLOVE_SetWindowPos(viewport, pos)
-- STUB: Set the OS-level window position (pos.x, pos.y).
end
local function ImGui_ImplLOVE_GetWindowPos(viewport)
-- STUB: Return the OS-level window position as ImVec2(x, y).
return ImVec2(0, 0)
end
local function ImGui_ImplLOVE_SetWindowSize(viewport, size)
-- STUB: Resize the OS-level window to (size.x, size.y).
end
local function ImGui_ImplLOVE_GetWindowSize(viewport)
-- STUB: Return the OS-level window size.
return ImVec2(800, 600)
end
local function ImGui_ImplLOVE_SetWindowFocus(viewport)
-- STUB: Bring the OS-level window to focus.
end
local function ImGui_ImplLOVE_GetWindowFocus(viewport)
-- STUB: Return true if this OS-level window has focus.
return false
end
local function ImGui_ImplLOVE_SetWindowTitle(viewport, title)
-- STUB: Set the title of the OS-level window.
end
local function ImGui_ImplLOVE_RenderWindow(viewport)
-- STUB: Render routine for the secondary viewport.
-- Set render target to this window's canvas/context, then call ImGui_ImplLOVE_RenderDrawData(viewport.DrawData)
end
local function ImGui_ImplLOVE_SwapBuffers(viewport)
-- STUB: OS-level call to swap buffers for this specific window context.
end
local function ImGui_ImplGMOD_UpdateMonitors()
local bd = ImGui_ImplLOVE_GetBackendData()
local io = ImGui.GetPlatformIO()
io.Monitors:resize(0)
local imgui_monitor = ImGuiPlatformMonitor()
ImVec2_Copy(imgui_monitor.MainSize, ImVec2(800, 600))
ImVec2_Copy(imgui_monitor.WorkSize, ImVec2(800, 600))
io.Monitors:push_back(imgui_monitor)
bd.WantUpdateMonitors = false
end
local function ImGui_ImplLOVE_InitMultiViewportSupport()
local platform_io = ImGui.GetPlatformIO()
platform_io.Platform_CreateWindow = ImGui_ImplLOVE_CreateWindow
platform_io.Platform_DestroyWindow = ImGui_ImplLOVE_DestroyWindow
platform_io.Platform_ShowWindow = ImGui_ImplLOVE_ShowWindow
platform_io.Platform_SetWindowPos = ImGui_ImplLOVE_SetWindowPos
platform_io.Platform_GetWindowPos = ImGui_ImplLOVE_GetWindowPos
platform_io.Platform_SetWindowSize = ImGui_ImplLOVE_SetWindowSize
platform_io.Platform_GetWindowSize = ImGui_ImplLOVE_GetWindowSize
platform_io.Platform_SetWindowFocus = ImGui_ImplLOVE_SetWindowFocus
platform_io.Platform_GetWindowFocus = ImGui_ImplLOVE_GetWindowFocus
platform_io.Platform_SetWindowTitle = ImGui_ImplLOVE_SetWindowTitle
platform_io.Renderer_RenderWindow = ImGui_ImplLOVE_RenderWindow
platform_io.Renderer_SwapBuffers = ImGui_ImplLOVE_SwapBuffers
local main_viewport = ImGui.GetMainViewport()
main_viewport.PlatformUserData = { Owned = false }
end
---------------------------------------------------------
-- PLATFORM DATA & CALLBACKS
---------------------------------------------------------
local function ImGui_ImplLOVE_PlatformSetImeData(ctx, vp, data)
-- Unlike GMod's invisible VGUI TextEntry workaround[cite: 1],
-- Love2D has native IME support we can toggle directly.
if data.WantVisible or data.WantTextInput then
love.keyboard.setTextInput(true, data.InputPos.x, data.InputPos.y, 1, data.InputLineHeight)
else
love.keyboard.setTextInput(false)
end
end
local function ImGui_ImplLOVE_PlatformSetClipboardText(ctx, text)
love.system.setClipboardText(text)
end
local function ImGui_ImplLOVE_PlatformGetClipboardText(ctx)
return love.system.getClipboardText()
end
local function ImGui_ImplLOVE_OpenInShellFn(ctx, path)
love.system.openURL(path)
end
---------------------------------------------------------
-- TEXTURE MANAGEMENT
---------------------------------------------------------
local ffi = require("ffi")
function ImGui_ImplLOVE_UpdateTexture(tex)
if tex.Status == ImTextureStatus.WantCreate then
local w, h = tex.Width, tex.Height
local row, row_base = tex:GetPixelsAt(0, 0)
local imgData = love.image.newImageData(w, h)
local ptr = ffi.cast("uint8_t*", imgData:getFFIPointer())
-- Iterate through the Lua table to manually assign the bytes to the C pointer
local total_bytes = w * h * 4
for i = 0, total_bytes - 1 do
ptr[i] = row[row_base + i]
end
local loveTex = love.graphics.newImage(imgData)
loveTex:setFilter("nearest", "nearest")
tex.BackendUserData = loveTex
tex:SetTexID(1)
tex:SetStatus(ImTextureStatus.OK)
elseif tex.Status == ImTextureStatus.WantUpdates then
local loveTex = tex.BackendUserData
IM_ASSERT(loveTex ~= nil)
for _, r in tex.Updates:iter() do
local w, h = r.w, r.h
local updateData = love.image.newImageData(w, h)
local dst = ffi.cast("uint8_t*", updateData:getFFIPointer())
for y = 0, h - 1 do
local src, src_base = tex:GetPixelsAt(r.x, r.y + y)
local dst_base = y * w * 4
for i = 0, w * 4 - 1 do
dst[dst_base + i] = src[src_base + i]
end
end
loveTex:replacePixels(
updateData,
nil,
1,
r.x,
r.y,
false
)
updateData:release()
end
tex:SetStatus(ImTextureStatus.OK)
elseif tex.Status == ImTextureStatus.WantDestroy then
if tex.BackendUserData then
tex.BackendUserData:release()
tex.BackendUserData = nil
end
tex:SetTexID(ImTextureID_Invalid)
tex:SetStatus(ImTextureStatus.Destroyed)
end
end
---------------------------------------------------------
-- RENDERING
---------------------------------------------------------
local rshift, band = bit32.rshift, bit32.band
local function u32ToRGBA(col)
-- Extract bytes from u32 integer just like GMod implementation[cite: 1]
return band(col, 0xFF) / 255,
band(rshift(col, 8), 0xFF) / 255,
band(rshift(col, 16), 0xFF) / 255,
band(rshift(col, 24), 0xFF) / 255
end
function ImGui_ImplLOVE_RenderDrawData(draw_data)
if draw_data.DisplaySize.x <= 0.0 or draw_data.DisplaySize.y <= 0.0 then return end
if draw_data.Textures ~= nil then
for _, tex in draw_data.Textures:iter() do
if tex.Status ~= ImTextureStatus.OK then
ImGui_ImplLOVE_UpdateTexture(tex)
end
end
end
love.graphics.push("all")
love.graphics.setBlendMode("alpha", "alphamultiply")
local format = {
{"VertexPosition", "float", 2},
{"VertexTexCoord", "float", 2},
{"VertexColor", "byte", 4},
}
local idx_data, vtx_data
for _, draw_list in draw_data.CmdLists:iter() do
idx_data = draw_list.IdxBuffer; vtx_data = draw_list.VtxBuffer
for _, pcmd in draw_list.CmdBuffer:iter() do
if pcmd.UserCallback ~= nil then
pcmd.UserCallback(draw_list, pcmd)
elseif pcmd.ElemCount > 0 and pcmd.ClipRect.z > pcmd.ClipRect.x and pcmd.ClipRect.w > pcmd.ClipRect.y then
-- LÖVE Scissor mapping
love.graphics.setScissor(pcmd.ClipRect.x, pcmd.ClipRect.y,
pcmd.ClipRect.z - pcmd.ClipRect.x, pcmd.ClipRect.w - pcmd.ClipRect.y)
-- Create a temporary table for vertices
-- Note: For production use, passing `ByteData` with an FFI pointer directly to `newMesh` is significantly faster.
local vertices = {}
for i = 0, pcmd.ElemCount - 1, 3 do
local idx0 = idx_data[pcmd.IdxOffset + 1 + i]
local idx1 = idx_data[pcmd.IdxOffset + 2 + i]
local idx2 = idx_data[pcmd.IdxOffset + 3 + i]
local vtx0 = vtx_data[pcmd.VtxOffset + idx0]
local vtx1 = vtx_data[pcmd.VtxOffset + idx1]
local vtx2 = vtx_data[pcmd.VtxOffset + idx2]
local r0, g0, b0, a0 = u32ToRGBA(vtx0[3])
table.insert(vertices, {vtx0[1][1], vtx0[1][2], vtx0[2][1], vtx0[2][2], r0, g0, b0, a0})
local r1, g1, b1, a1 = u32ToRGBA(vtx1[3])
table.insert(vertices, {vtx1[1][1], vtx1[1][2], vtx1[2][1], vtx1[2][2], r1, g1, b1, a1})
local r2, g2, b2, a2 = u32ToRGBA(vtx2[3])
table.insert(vertices, {vtx2[1][1], vtx2[1][2], vtx2[2][1], vtx2[2][2], r2, g2, b2, a2})
end
local mesh = love.graphics.newMesh(format, vertices, "triangles")
-- The Texture object is stored in BackendUserData
local tex_id = pcmd:GetTexID()
local tex = ImGui.GetPlatformIO().Textures[tex_id]
if tex and tex.BackendUserData then
mesh:setTexture(tex.BackendUserData)
end
love.graphics.draw(mesh)
end
end
end
love.graphics.setScissor()
love.graphics.pop()
end
---------------------------------------------------------
-- CORE LIFECYCLE
---------------------------------------------------------
function ImGui_ImplLOVE_Init()
local io = ImGui.GetIO()
local bd = { Time = love.timer.getTime() }
io.BackendPlatformUserData = bd
--io.BackendFlags = bit32.bor(io.BackendFlags, ImGuiBackendFlags.PlatformHasViewports)
io.BackendFlags = bit32.bor(io.BackendFlags, ImGuiBackendFlags.RendererHasTextures)
io.BackendFlags = bit32.bor(io.BackendFlags, ImGuiBackendFlags.RendererHasVtxOffset)
--io.BackendFlags = bit32.bor(io.BackendFlags, ImGuiBackendFlags.RendererHasViewports)
local platform_io = ImGui.GetPlatformIO()
platform_io.Platform_SetImeDataFn = ImGui_ImplLOVE_PlatformSetImeData
platform_io.Platform_SetClipboardTextFn = ImGui_ImplLOVE_PlatformSetClipboardText
platform_io.Platform_GetClipboardTextFn = ImGui_ImplLOVE_PlatformGetClipboardText
platform_io.Platform_OpenInShellFn = ImGui_ImplLOVE_OpenInShellFn
--ImGui_ImplLOVE_InitMultiViewportSupport()
ImGui_ImplGMOD_UpdateMonitors()
end
function ImGui_ImplLOVE_Shutdown()
local io = ImGui.GetIO()
ImGui.DestroyPlatformWindows()
io.BackendPlatformUserData = nil
io.BackendFlags = bit32.band(io.BackendFlags, bit32.bnot(bit32.bor(
ImGuiBackendFlags.HasMouseCursors,
ImGuiBackendFlags.PlatformHasViewports,
ImGuiBackendFlags.RendererHasViewports
)))
end
function ImGui_ImplLOVE_NewFrame()
local io = ImGui.GetIO()
local bd = ImGui_ImplLOVE_GetBackendData()
local w, h = love.graphics.getDimensions()
ImVec2_CopyV(io.DisplaySize, w, h)
local current_time = love.timer.getTime()
io.DeltaTime = (bd.Time > 0.0) and (current_time - bd.Time) or (1.0 / 60.0)
bd.Time = current_time
-- Update Cursor
if bit32.band(io.ConfigFlags, ImGuiConfigFlags.NoMouseCursorChange) == 0 then
local cursor = CURSOR_MAP[ImGui.GetMouseCursor()]
if cursor then
love.mouse.setCursor(love.mouse.getSystemCursor(cursor))
else
love.mouse.setCursor()
end
end
end
---------------------------------------------------------
-- LOVE EVENT HOOKS (Call these from main.lua)
---------------------------------------------------------
local function Hook_MouseMoved(x, y)
local io = ImGui.GetIO()
io:AddMouseSourceEvent(ImGuiMouseSource.Mouse)
io:AddMousePosEvent(x, y)
end
local function Hook_MousePressed(x, y, button)
local io = ImGui.GetIO()
io:AddMouseSourceEvent(ImGuiMouseSource.Mouse)
-- LÖVE passes buttons as 1=left, 2=right, 3=middle. ImGui uses 0=left, 1=right, 2=middle.
if button <= 3 then
local ig_btn = (button == 1) and ImGuiMouseButton.Left or (button == 2 and ImGuiMouseButton.Right or ImGuiMouseButton.Middle)
io:AddMouseButtonEvent(ig_btn, true)
end
end
local function Hook_MouseReleased(x, y, button)
local io = ImGui.GetIO()
if button <= 3 then
local ig_btn = (button == 1) and ImGuiMouseButton.Left or (button == 2 and ImGuiMouseButton.Right or ImGuiMouseButton.Middle)
io:AddMouseButtonEvent(ig_btn, false)
end
end
local function Hook_WheelMoved(x, y)
local io = ImGui.GetIO()
io:AddMouseWheelEvent(x, y)
end
local function Hook_KeyPressed(key)
local io = ImGui.GetIO()
ImGui_ImplLOVE_UpdateKeyModifiers()
if KEY_MAP[key] then
io:AddKeyEvent(KEY_MAP[key], true)
end
end
local function Hook_KeyReleased(key)
local io = ImGui.GetIO()
ImGui_ImplLOVE_UpdateKeyModifiers()
if KEY_MAP[key] then
io:AddKeyEvent(KEY_MAP[key], false)
end
end
local function Hook_TextInput(text)
local io = ImGui.GetIO()
for _, code in utf8.codes(text) do
io:AddInputCharacter(code)
end
end
--Implementing some bullshit
function ImStd.ImFileOpen(_filename, _mode)
if _mode == "rb" then _mode = "r" end
local file, _ = love.filesystem.newFile(_filename, _mode)
return file
end
function ImStd.ImFileClose(f)
f:close()
end
function ImStd.ImFileGetSize(f)
return f:getSize()
end
function ImStd.ImFileRead(f, data, count)
local CHUNK_SIZE = 8000 -- We don't read byte by byte
local offset = 0
while offset < count do
local read_size = math.min(CHUNK_SIZE, count - offset)
local str = f:read(read_size)
-- Handle EOF or read failure gracefully
if not str or #str == 0 then break end
-- Iterate based on actual bytes read without risking stack overflow
for i = 1, #str do
data[offset + i] = string.byte(str, i)
end
offset = offset + #str
end
end
function ImStd.ImFileLoadToMemory(filename, mode)
local f = ImStd.ImFileOpen(filename, mode)
if not f then return end
local file_size = ImStd.ImFileGetSize(f)
if file_size <= 0 then
ImStd.ImFileClose(f)
return
end
local file_data = {}
ImStd.ImFileRead(f, file_data, file_size)
if #file_data == 0 then
ImStd.ImFileClose(f)
return
end
ImStd.ImFileClose(f)
return file_data, file_size
end
ImGui_ImplLove = {
Init = ImGui_ImplLOVE_Init,
Shutdown = ImGui_ImplLOVE_Shutdown,
NewFrame = ImGui_ImplLOVE_NewFrame,
RenderDrawData = ImGui_ImplLOVE_RenderDrawData,
-- Event hooks to connect in love.* callbacks
MouseMoved = Hook_MouseMoved,
MousePressed = Hook_MousePressed,
MouseReleased = Hook_MouseReleased,
WheelMoved = Hook_WheelMoved,
KeyPressed = Hook_KeyPressed,
KeyReleased = Hook_KeyReleased,
TextInput = Hook_TextInput,
}
+11284
View File
File diff suppressed because it is too large Load Diff
+974
View File
@@ -0,0 +1,974 @@
if _G.__IMGUI_ENV then setfenv(1, _G.__IMGUI_ENV) end -- [Roblox] shared env, see init.lua
--- ImGui Sincerely WIP
-- (Demo Code)
-- NOTE: Make sure to use fields with different names inside `static`!
local IM_MIN = math.min
local IM_MAX = math.max
local function IM_CLAMP(V, MN, MX) return (V < MN) and MN or (V > MX) and MX or V end
-- TODO: HelpMarker()
-- TODO: ImColor helpers?
--- @class ImGuiDemoWindowData
--- @return ImGuiDemoWindowData
--- @nodiscard
local function ImGuiDemoWindowData()
return {
ShowAppImageViewer = false,
ShowAppFullscreen = false,
ShowMetrics = false,
ShowStyleEditor = false,
}
end
--- @class ExampleImageViewerData
--- @return ExampleImageViewerData
--- @nodiscard
local function ExampleImageViewerData()
return {
ImageBgColor = IM_COL32(100, 100, 100, 255),
GridColor = IM_COL32(255, 255, 255, 100),
GridEnabled = true,
ViewReset = true,
ViewOffset = ImVec2(0, 0),
Zoom = 10.0,
ZoomMin = 1.0,
ZoomMax = 10000.0
}
end
--- @param data ExampleImageViewerData
local function ExampleImageViewer_DrawOptions(data)
ImGui.SetNextItemShortcut(ImGuiKey.G, ImGuiInputFlags.Tooltip)
_, data.GridEnabled = ImGui.Checkbox("Grid", data.GridEnabled)
ImGui.SameLine()
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 10.0)
local zoom_100 = data.Zoom * 100.0
local changed
zoom_100, changed = ImGui.DragFloat("##Zoom", zoom_100, 5.0, data.ZoomMin * 100.0, data.ZoomMax * 100.0, "%.0f%%", ImGuiSliderFlags.AlwaysClamp)
if changed then
data.Zoom = zoom_100 / 100.0
end
end
--- @param data ExampleImageViewerData
--- @param canvas_size ImVec2
--- @param image_tex_ref ImTextureRef
--- @param image_w int
--- @param image_h int
local function ExampleImageViewer_DrawCanvas(data, canvas_size, image_tex_ref, image_w, image_h)
local io = ImGui.GetIO()
local platform_io = ImGui.GetPlatformIO()
local draw_list = ImGui.GetWindowDrawList()
IM_ASSERT(canvas_size.x >= 0.0 and canvas_size.y >= 0.0)
ImGui.InvisibleButton("##Canvas", canvas_size)
local canvas_min = ImGui.GetItemRectMin()
local canvas_max = ImGui.GetItemRectMax()
if data.ViewReset then
data.ViewOffset = ImVec2((canvas_size.x * 0.5 / data.Zoom) - 0.5, (canvas_size.y * 0.5 / data.Zoom) - 0.5)
end
data.ViewReset = false
if ImGui.SetItemKeyOwner(ImGuiKey.MouseWheelY) then
if io.MouseWheel ~= 0.0 then
data.Zoom = IM_CLAMP(data.Zoom * (1.0 + io.MouseWheel * 0.10), data.ZoomMin, data.ZoomMax)
end
end
local zoom = data.Zoom
if ImGui.IsItemActive() and ImGui.IsMouseDragging(0) then
data.ViewOffset.x = data.ViewOffset.x - io.MouseDelta.x / zoom
data.ViewOffset.y = data.ViewOffset.y - io.MouseDelta.y / zoom
end
local image_min = ImVec2(); local image_max = ImVec2()
image_min.x = math.floor((canvas_min.x - (data.ViewOffset.x * zoom)) + (canvas_size.x * 0.5))
image_min.y = math.floor((canvas_min.y - (data.ViewOffset.y * zoom)) + (canvas_size.y * 0.5))
image_max.x = math.floor(image_min.x + image_w * zoom)
image_max.y = math.floor(image_min.y + image_h * zoom)
draw_list:AddRect(ImVec2(canvas_min.x - 1.0, canvas_min.y - 1.0), ImVec2(canvas_max.x + 1.0, canvas_max.y + 1.0), IM_COL32(255, 255, 255, 255))
draw_list:PushClipRect(canvas_min, canvas_max, true)
draw_list:AddRectFilled(image_min, image_max, data.ImageBgColor)
if platform_io.DrawCallback_SetSamplerNearest ~= nil then
draw_list:AddCallback(platform_io.DrawCallback_SetSamplerNearest)
end
draw_list:AddImage(image_tex_ref, image_min, image_max)
if platform_io.DrawCallback_SetSamplerLinear ~= nil then
draw_list:AddCallback(ImGui.GetPlatformIO().DrawCallback_SetSamplerLinear)
end
if data.GridEnabled and zoom > 6.0 then
local step = zoom
for px = math.floor((canvas_min.x - image_min.x) / step), math.floor((canvas_max.x - image_min.x) / step) do
draw_list:AddLineV(image_min.x + px * step, canvas_min.y, canvas_max.y, data.GridColor, 1.0)
end
for py = math.floor((canvas_min.y - image_min.y) / step), math.floor((canvas_max.y - image_min.y) / step) do
draw_list:AddLineH(canvas_min.x, canvas_max.x, image_min.y + py * step, data.GridColor, 1.0)
end
end
draw_list:PopClipRect()
end
local function ShowExampleMenuFile()
ImGui.MenuItem("(demo menu)", nil, false, false)
if ImGui.MenuItem("New") then end
if ImGui.MenuItem("Open", "Ctrl+O") then end
if ImGui.BeginMenu("Open Recent") then
ImGui.MenuItem("fish_hat.c")
ImGui.MenuItem("fish_hat.inl")
ImGui.MenuItem("fish_hat.h")
if ImGui.BeginMenu("More..") then
ImGui.MenuItem("Hello")
ImGui.MenuItem("Sailor")
if ImGui.BeginMenu("Recurse..") then
ShowExampleMenuFile()
ImGui.EndMenu()
end
ImGui.EndMenu()
end
ImGui.EndMenu()
end
if ImGui.MenuItem("Save", "Ctrl+S") then end
if ImGui.MenuItem("Save As..") then end
end
local function DemoWindowMenuBar(demo_data)
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("Menu") then
ShowExampleMenuFile()
ImGui.EndMenu()
end
if ImGui.BeginMenu("Examples") then
ImGui.SeparatorText("Concepts")
_, demo_data.ShowAppFullscreen = ImGui.MenuItem("Fullscreen window", nil, demo_data.ShowAppFullscreen)
ImGui.EndMenu()
end
if ImGui.BeginMenu("Tools") then
local has_debug_tools = false
if not IMGUI_DISABLE_DEBUG_TOOLS then
has_debug_tools = true
end
_, demo_data.ShowMetrics = ImGui.MenuItem("Metrics/Debugger", nil, demo_data.ShowMetrics, has_debug_tools)
_, demo_data.ShowStyleEditor = ImGui.MenuItem("Style Editor", nil, demo_data.ShowStyleEditor)
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
end
local DemoWindowWidgetsBasic
do
local static = {}
local clicked = 0
local checked = true
local radio_v = 0
local col0 = ImVec4(0, 0, 0, 1)
local col1 = ImVec4(0, 0, 0, 1)
local col2 = ImVec4(0, 0, 0, 1)
local counter = 0
local str0 = {string.byte("Hello, World!", 1, 13)}
table.insert(str0, 0)
local str1 = {}
str1[1] = 0
local hint0 = {string.byte("enter text here", 1, 15)}
table.insert(hint0, 0)
local vec4a = { 0.10, 0.20, 0.30, 0.44 }
local Element = { Fire = 1, Earth = 2, Air = 3, Water = 4, COUNT = 5 }
local elems_names = { "Fire", "Earth", "Air", "Water" }
function DemoWindowWidgetsBasic()
if ImGui.TreeNode("Basic") then
ImGui.SeparatorText("General")
if ImGui.Button("Button") then
clicked = clicked + 1
end
if clicked % 2 ~= 0 then
ImGui.SameLine()
ImGui.Text("Thanks for clicking me!")
end
_, check = ImGui.Checkbox("checkbox", check)
_, radio_v = ImGui.RadioButton("radio a", radio_v, 0) ImGui.SameLine()
_, radio_v = ImGui.RadioButton("radio b", radio_v, 1) ImGui.SameLine()
_, radio_v = ImGui.RadioButton("radio c", radio_v, 2)
ImGui.AlignTextToFramePadding()
ImGui.TextLinkOpenURL("Hyperlink", "https://github.com/GrayWolf64/imgui-lua")
for i = 1, 7 do
if i > 1 then
ImGui.SameLine()
end
ImGui.PushID(i)
col0.x, col0.y, col0.z = ImGui.ColorConvertHSVtoRGB(i / 7.0, 0.6, 0.6)
col1.x, col1.y, col1.z = ImGui.ColorConvertHSVtoRGB(i / 7.0, 0.7, 0.7)
col2.x, col2.y, col2.z = ImGui.ColorConvertHSVtoRGB(i / 7.0, 0.8, 0.8)
ImGui.PushStyleColor(ImGuiCol.Button, col0)
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, col1)
ImGui.PushStyleColor(ImGuiCol.ButtonActive, col2)
ImGui.Button("Click")
ImGui.PopStyleColor(3)
ImGui.PopID()
end
-- Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
-- (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
-- See 'Demo->Layout->Text Baseline Alignment' for details.
ImGui.AlignTextToFramePadding()
ImGui.Text("Hold to repeat:")
ImGui.SameLine()
-- Arrow buttons with Repeater
local spacing = ImGui.GetStyle().ItemInnerSpacing.x
ImGui.PushItemFlag(ImGuiItemFlags.ButtonRepeat, true)
if ImGui.ArrowButton("##left", ImGuiDir.Left) then counter = counter - 1 end
ImGui.SameLine(0.0, spacing)
if ImGui.ArrowButton("##right", ImGuiDir.Right) then counter = counter + 1 end
ImGui.PopItemFlag()
ImGui.SameLine()
ImGui.Text("%d", counter)
ImGui.Button("Tooltip")
ImGui.SetItemTooltip("I am a tooltip")
ImGui.LabelText("label", "Value")
ImGui.SeparatorText("Inputs")
do
ImGui.InputText("input text", str0, 128)
ImGui.InputTextWithHint("input text (w/ hint)", hint0, str1, 128)
if not static.i0 then static.i0 = 233 end
static.i0 = ImGui.InputInt("input int", static.i0)
if not static.f0 then static.f0 = 0.001 end
static.f0 = ImGui.InputFloat("input float", static.f0, 0.01, 1.0)
if not static.d0 then static.d0 = 999999.00000001 end
static.d0 = ImGui.InputDouble("input double", static.d0, 0.01, 1.0, "%.8f")
-- TODO:
ImGui.InputFloat3("input float3", vec4a)
end
ImGui.SeparatorText("Drags")
do
if not static.i1 then static.i1 = 50 end
if not static.i2 then static.i2 = 42 end
if not static.i3 then static.i3 = 128 end
static.i1 = ImGui.DragInt("drag int", static.i1, 1)
static.i2 = ImGui.DragInt("drag int 0..100", static.i2, 1, 0, 100, "%d%%", ImGuiSliderFlags.AlwaysClamp)
static.i3 = ImGui.DragInt("drag int wrap 100..200", static.i3, 1, 100, 200, "%d", ImGuiSliderFlags.WrapAround)
if not static.f1 then static.f1 = 1.00 end
if not static.f2 then static.f2 = 0.0067 end
static.f1 = ImGui.DragFloat("drag float", static.f1, 0.005)
static.f2 = ImGui.DragFloat("drag small float", static.f2, 0.0001, 0.0, 0.0, "%.06f ns")
end
ImGui.SeparatorText("Sliders")
do
if not static.f01 then static.f01 = 0.123 end
if not static.f02 then static.f02 = 0.0 end
static.f01 = ImGui.SliderFloat("slider float", static.f01, 0.0, 1.0, "ratio = %.3f")
static.f02 = ImGui.SliderFloat("slider float (log)", static.f02, -10.0, 10.0, "%.4f", ImGuiSliderFlags.Logarithmic)
if not static.angle then static.angle = 0.0 end
static.angle = ImGui.SliderAngle("slider angle", static.angle)
if not static.elem then static.elem = Element.Fire end
local elem_name = (static.elem >= 1 and static.elem < Element.COUNT) and elems_names[static.elem] or "Unknown"
static.elem = ImGui.SliderInt("slider enum", static.elem, 1, Element.COUNT - 1, elem_name)
end
ImGui.TreePop()
end
end
end
local function DemoWindowWidgetsBullets()
if ImGui.TreeNode("Bullets") then
ImGui.BulletText("Bullet point 1")
ImGui.BulletText("Bullet point 2\nOn multiple lines")
if ImGui.TreeNode("Tree node") then
ImGui.BulletText("Another bullet point")
ImGui.TreePop()
end
ImGui.Bullet() ImGui.Text("Bullet point 3 (two calls)")
ImGui.Bullet() ImGui.SmallButton("Button")
ImGui.TreePop()
end
end
local DemoWindowWidgetsColorAndPickers
do
local color = {114.0 / 255.0, 144.0 / 255.0, 154.0 / 255.0, 200.0 / 255.0}
local base_flags = ImGuiColorEditFlags.None
local ref_color = false
local ref_color_v = {1.0, 0.0, 1.0, 0.5}
local picker_mode = 0
local display_mode = 0
local color_picker_flags = ImGuiColorEditFlags.AlphaBar
local picker_mode_names = {"Auto/Current", "ImGuiColorEditFlags.PickerHueBar", "ImGuiColorEditFlags.PickerHueWheel"}
local display_mode_names = {"Auto/Current", "ImGuiColorEditFlags.NoInputs", "ImGuiColorEditFlags.DisplayRGB", "ImGuiColorEditFlags.DisplayHSV", "ImGuiColorEditFlags.DisplayHex"}
function DemoWindowWidgetsColorAndPickers()
if ImGui.TreeNode("Color/Picker Widgets") then
ImGui.SeparatorText("Options")
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.NoAlpha", base_flags, ImGuiColorEditFlags.NoAlpha)
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.AlphaOpaque", base_flags, ImGuiColorEditFlags.AlphaOpaque)
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.AlphaNoBg", base_flags, ImGuiColorEditFlags.AlphaNoBg)
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.AlphaPreviewHalf", base_flags, ImGuiColorEditFlags.AlphaPreviewHalf)
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.NoOptions", base_flags, ImGuiColorEditFlags.NoOptions)
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.NoDragDrop", base_flags, ImGuiColorEditFlags.NoDragDrop)
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.NoColorMarkers", base_flags, ImGuiColorEditFlags.NoColorMarkers)
_, base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.HDR", base_flags, ImGuiColorEditFlags.HDR)
ImGui.SeparatorText("Inline color editor")
ImGui.Text("Color widget:")
ImGui.ColorEdit3("MyColor##1", color, base_flags)
ImGui.Text("Color widget HSV with Alpha:")
ImGui.ColorEdit4("MyColor##2", color, bit32.bor(ImGuiColorEditFlags.DisplayHSV, base_flags))
ImGui.Text("Color widget with Float Display:")
ImGui.ColorEdit4("MyColor##2f", color, bit32.bor(ImGuiColorEditFlags.Float, base_flags))
ImGui.Text("Color button with Picker:")
ImGui.ColorEdit4("MyColor##3", color, bit32.bor(ImGuiColorEditFlags.NoInputs, ImGuiColorEditFlags.NoLabel, base_flags))
-- TODO:
ImGui.SeparatorText("Color picker")
ImGui.PushID("Color picker")
_, color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.NoAlpha", color_picker_flags, ImGuiColorEditFlags.NoAlpha)
_, color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.AlphaBar", color_picker_flags, ImGuiColorEditFlags.AlphaBar)
_, color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.NoSidePreview", color_picker_flags, ImGuiColorEditFlags.NoSidePreview)
if bit32.band(color_picker_flags, ImGuiColorEditFlags.NoSidePreview) ~= 0 then
ImGui.SameLine()
_, ref_color = ImGui.Checkbox("With Ref Color", ref_color)
if ref_color then
ImGui.SameLine()
ImGui.ColorEdit4("##RefColor", ref_color_v, bit32.bor(ImGuiColorEditFlags.NoInputs, base_flags))
end
end
_, color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags.PickerNoRotate", color_picker_flags, ImGuiColorEditFlags.PickerNoRotate)
if ImGui.BeginCombo("Picker Mode", picker_mode_names[picker_mode + 1], ImGuiComboFlags.None) then
for mode_idx, mode_name in ipairs(picker_mode_names) do
local pressed = ImGui.Selectable(mode_name, mode_idx == picker_mode + 1)
if pressed then
picker_mode = mode_idx - 1
end
end
ImGui.EndCombo()
end
if ImGui.BeginCombo("Display Mode", display_mode_names[display_mode + 1], ImGuiComboFlags.None) then
for mode_idx, mode_name in ipairs(display_mode_names) do
local pressed = ImGui.Selectable(mode_name, mode_idx == display_mode + 1)
if pressed then
display_mode = mode_idx - 1
end
end
ImGui.EndCombo()
end
local flags = bit32.bor(base_flags, color_picker_flags)
if picker_mode == 1 then flags = bit32.bor(flags, ImGuiColorEditFlags.PickerHueBar) end
if picker_mode == 2 then flags = bit32.bor(flags, ImGuiColorEditFlags.PickerHueWheel) end
if display_mode == 1 then flags = bit32.bor(flags, ImGuiColorEditFlags.NoInputs) end -- Disable all RGB/HSV/Hex displays
if display_mode == 2 then flags = bit32.bor(flags, ImGuiColorEditFlags.DisplayRGB) end -- Override display mode
if display_mode == 3 then flags = bit32.bor(flags, ImGuiColorEditFlags.DisplayHSV) end
if display_mode == 4 then flags = bit32.bor(flags, ImGuiColorEditFlags.DisplayHex) end
ImGui.ColorPicker4("MyColor##4", color, flags, ref_color and ref_color_v or nil)
ImGui.TreePop()
end
end
end
local DemoWindowWidgetsImages do
local pressed_count = 0
local image_viewer = ExampleImageViewerData()
function DemoWindowWidgetsImages()
if ImGui.TreeNode("Images") then
ImGui.TextWrapped(
"Below we are displaying the font texture (which is the only texture we have access to in this demo). " ..
"Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. " ..
"Hover the texture for a zoomed view!"
)
-- Grab the current texture identifier used by the font atlas
local io = ImGui.GetIO()
local atlas = io.Fonts
local my_tex_id = atlas.TexRef
local my_tex_w = atlas.TexData.Width
local my_tex_h = atlas.TexData.Height
ImGui.Text("%.0fx%.0f", my_tex_w, my_tex_h)
ImGui.SeparatorText("Image()/ImageWithBg() function")
local uv_min = ImVec2(0.0, 0.0)
local uv_max = ImVec2(1.0, 1.0)
ImGui.PushStyleVar(ImGuiStyleVar.ImageBorderSize, IM_MAX(1.0, ImGui.GetStyle().ImageBorderSize))
ImGui.ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0, 0.0, 0.0, 1.0))
ImGui.PopStyleVar()
-- Fancy widget
ImGui.SeparatorText("Interactive Image Viewer")
local canvas_size = ImVec2(ImGui.GetContentRegionAvail().x, my_tex_h * 2.0)
ExampleImageViewer_DrawOptions(image_viewer)
ExampleImageViewer_DrawCanvas(image_viewer, canvas_size, my_tex_id, my_tex_w, my_tex_h)
ImGui.SeparatorText("Textured Buttons")
ImGui.TextWrapped("And now some textured buttons..")
for i = 1, 8 do
-- UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.
-- Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.
ImGui.PushID(i)
if i > 1 then
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ImVec2(i, i))
end
local size = ImVec2(32.0, 32.0) -- Size of the image we want to make visible
local uv0 = ImVec2(0.0, 0.0) -- UV coordinates for lower-left
local uv1 = ImVec2(32.0 / my_tex_w, 32.0 / my_tex_h) -- UV coordinates for (32,32) in our texture
local bg_col = ImVec4(0.0, 0.0, 0.0, 1.0) -- Black background
local tint_col = ImVec4(1.0, 1.0, 1.0, 1.0) -- No tint
if ImGui.ImageButton("", my_tex_id, size, uv0, uv1, bg_col, tint_col) then
pressed_count = pressed_count + 1
end
if i > 1 then
ImGui.PopStyleVar()
end
ImGui.PopID()
ImGui.SameLine()
end
ImGui.NewLine()
ImGui.Text("Pressed %d times.", pressed_count)
ImGui.TreePop()
end
end
end
local function DemoWindowWidgetsLiveEdit()
-- TODO:
end
local DemoWindowWidgetsPlotting
do
local animate = true
local arr = { 0.6, 0.1, 1.0, 0.5, 0.92, 0.1, 0.2 }
local values_sz = 90
local values = {} for i = 1, values_sz do values[i] = 0 end
local values_offset = 0
local refresh_time = 0.0
local phase = 0.0
function DemoWindowWidgetsPlotting()
if ImGui.TreeNode("Plotting") then
_, animate = ImGui.Checkbox("Animate", animate)
-- Plot as lines and plot as histogram
ImGui.PlotLines("Frame Times", arr, nil, #arr)
ImGui.PlotHistogram("Histogram", arr, nil, #arr, 0, nil, 0.0, 1.0, ImVec2(0, 80.0))
if not animate or refresh_time == 0.0 then
refresh_time = ImGui.GetTime()
end
while refresh_time < ImGui.GetTime() do -- Create data at fixed 60 Hz rate for the demo
values[values_offset + 1] = math.cos(phase)
values_offset = (values_offset + 1) % values_sz
phase = phase + 0.10 * values_offset
refresh_time = refresh_time + 1.0 / 60.0
end
-- Plots can display overlay texts
-- (in this example, we will display an average value)
do
local average = 0.0
for i = 1, values_sz do
average = average + values[i]
end
average = average / values_sz
local overlay = string.format("avg %f", average)
ImGui.PlotLines("Lines", values, nil, values_sz, values_offset, overlay, -1.0, 1.0, ImVec2(0, 80.0))
end
ImGui.TreePop()
end
end
end
local DemoWindowWidgetsProgressBars do
local progress_accum = 0.0
local progress_dir = 1.0
function DemoWindowWidgetsProgressBars()
if ImGui.TreeNode("Progress Bars") then
-- Animate a simple progress bar
progress_accum = progress_accum + progress_dir * 0.4 * ImGui.GetIO().DeltaTime
if progress_accum >= 1.1 then
progress_accum = 1.1
progress_dir = progress_dir * -1.0
end
if progress_accum <= -0.1 then
progress_accum = -0.1
progress_dir = progress_dir * -1.0
end
local progress = IM_CLAMP(progress_accum, 0.0, 1.0)
-- Typically we would use ImVec2(-1.0,0.0) or ImVec2(-math.huge,0.0) to use all available width,
-- or ImVec2(width,0.0) for a specified width. ImVec2(0.0,0.0) uses ItemWidth
ImGui.ProgressBar(progress, ImVec2(0.0, 0.0))
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
ImGui.Text("Progress Bar")
local buf = string.format("%d/%d", math.floor(progress * 1753), 1753)
ImGui.ProgressBar(progress, ImVec2(0.0, 0.0), buf)
-- Pass an animated negative value, e.g. -1.0 * ImGui.GetTime() is the recommended value
-- Adjust the factor if you want to adjust the animation speed
ImGui.ProgressBar(-1.0 * ImGui.GetTime(), ImVec2(0.0, 0.0), "Searching..")
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
ImGui.Text("Indeterminate")
ImGui.TreePop()
end
end
end
local DemoWindowWidgetsVerticalSliders do
local int_value = 0
local values = { 0.0, 0.60, 0.35, 0.9, 0.70, 0.20, 0.0 }
local values2 = { 0.20, 0.80, 0.40, 0.25 }
local col0 = ImVec4(0, 0, 0, 1)
local col1 = ImVec4(0, 0, 0, 1)
local col2 = ImVec4(0, 0, 0, 1)
local col3 = ImVec4(0, 0, 0, 1)
function DemoWindowWidgetsVerticalSliders()
if ImGui.TreeNode("Vertical Sliders") then
local spacing = 4
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ImVec2(spacing, spacing))
int_value = ImGui.VSliderInt("##int", ImVec2(18, 160), int_value, 0, 5)
ImGui.SameLine()
ImGui.PushID("set1")
for i = 1, 7 do
if i > 1 then ImGui.SameLine() end
ImGui.PushID(i)
local hue = (i - 1) / 7.0
col0.x, col0.y, col0.z = ImGui.ColorConvertHSVtoRGB(hue, 0.5, 0.5)
col1.x, col1.y, col1.z = ImGui.ColorConvertHSVtoRGB(hue, 0.6, 0.5)
col2.x, col2.y, col2.z = ImGui.ColorConvertHSVtoRGB(hue, 0.7, 0.5)
col3.x, col3.y, col3.z = ImGui.ColorConvertHSVtoRGB(hue, 0.9, 0.9)
ImGui.PushStyleColor(ImGuiCol.FrameBg, col0)
ImGui.PushStyleColor(ImGuiCol.FrameBgHovered, col1)
ImGui.PushStyleColor(ImGuiCol.FrameBgActive, col2)
ImGui.PushStyleColor(ImGuiCol.SliderGrab, col3)
values[i] = ImGui.VSliderFloat("##v", ImVec2(18, 160), values[i], 0.0, 1.0, "")
if ImGui.IsItemActive() or ImGui.IsItemHovered() then
ImGui.SetTooltip("%.3f", values[i])
end
ImGui.PopStyleColor(4)
ImGui.PopID()
end
ImGui.PopID()
ImGui.SameLine()
ImGui.PushID("set2")
local rows = 3
local small_slider_size = ImVec2(18, math.floor((160.0 - (rows - 1) * spacing) / rows))
for nx = 1, 4 do
if nx > 1 then ImGui.SameLine() end
ImGui.BeginGroup()
for ny = 1, rows do
ImGui.PushID((nx - 1) * rows + ny)
values2[nx] = ImGui.VSliderFloat("##v", small_slider_size, values2[nx], 0.0, 1.0, "")
if ImGui.IsItemActive() or ImGui.IsItemHovered() then
ImGui.SetTooltip("%.3f", values2[nx])
end
ImGui.PopID()
end
ImGui.EndGroup()
end
ImGui.PopID()
ImGui.SameLine()
ImGui.PushID("set3")
for i = 1, 4 do
if i > 1 then ImGui.SameLine() end
ImGui.PushID(i)
ImGui.PushStyleVar(ImGuiStyleVar.GrabMinSize, 40)
values[i] = ImGui.VSliderFloat("##v", ImVec2(40, 160), values[i], 0.0, 1.0, "%.2f\nsec")
ImGui.PopStyleVar()
ImGui.PopID()
end
ImGui.PopID()
ImGui.PopStyleVar()
ImGui.TreePop()
end
end
end
local function DemoWindowWidgets()
if not ImGui.CollapsingHeader("Widgets") then
return
end
DemoWindowWidgetsBasic()
DemoWindowWidgetsBullets()
DemoWindowWidgetsColorAndPickers()
DemoWindowWidgetsImages()
DemoWindowWidgetsLiveEdit()
DemoWindowWidgetsPlotting()
DemoWindowWidgetsProgressBars()
DemoWindowWidgetsVerticalSliders()
end
local function DemoWindowLayout()
if not ImGui.CollapsingHeader("Layout & Scrolling") then
return
end
end
local function DemoWindowPopups()
if not ImGui.CollapsingHeader("Popups & Modal windows") then
return
end
end
local function DemoWindowTables()
if not ImGui.CollapsingHeader("Tables & Columns") then
return
end
end
local function DemoWindowInputs()
if not ImGui.CollapsingHeader("Inputs & Focus") then
return
end
end
do
local static = {}
local style_idx = -1
local style_names = { "Dark", "Light", "Classic" }
--- @param label string
function ImGui.ShowStyleSelector(label)
local ret = false
if ImGui.BeginCombo(label, (style_idx >= 1 and style_idx <= #style_names) and style_names[style_idx] or "") then
for n = 1, #style_names do
if ImGui.Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags.SelectOnNav) then
style_idx = n
ret = true
if style_idx == 1 then ImGui.StyleColorsDark() end
if style_idx == 2 then ImGui.StyleColorsLight() end
if style_idx == 3 then ImGui.StyleColorsClassic() end
elseif style_idx == n then
ImGui.SetItemDefaultFocus()
end
end
ImGui.EndCombo()
end
return ret
end
--- @param label string
function ImGui.ShowFontSelector(label)
local io = ImGui.GetIO()
local font_current = ImGui.GetFont()
if ImGui.BeginCombo(label, font_current:GetDebugName()) then
for _, font in io.Fonts.Fonts:iter() do
ImGui.PushID(tostring(font):match("0x%x+")) -- (void*)font
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
end
--- @param ref ImGuiStyle?
function ImGui.ShowStyleEditor(ref)
local style = ImGui.GetStyle()
local default_border_size = ImTrunc(style._MainScale)
local max_border_size = ImMax(default_border_size, 2.0)
ImGui.PushItemWidth(ImGui.GetWindowWidth() * 0.50)
ImGui.SeparatorText("General")
do
if ImGui.ShowStyleSelector("Colors##Selector") then
-- TODO:
end
ImGui.ShowFontSelector("Fonts##Selector")
local ret0
style.FontSizeBase, ret0 = ImGui.DragFloat("FontSizeBase", style.FontSizeBase, 0.20, 5.0, 100.0, "%.0f")
if ret0 then
style._NextFrameFontSizeBase = style.FontSizeBase -- FIXME: Temporary hack until we finish remaining work.
end
ImGui.SameLine(0.0, 0.0); ImGui.Text(" (out %.2f)", ImGui.GetFontSize())
style.FontScaleMain = ImGui.DragFloat("FontScaleMain", style.FontScaleMain, 0.02, 0.5, 4.0)
ImGui.BeginDisabled(ImGui.GetIO().ConfigDpiScaleFonts)
style.FontScaleDpi = ImGui.DragFloat("FontScaleDpi", style.FontScaleDpi, 0.02, 0.5, 4.0)
ImGui.SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.")
ImGui.EndDisabled()
local ret1
style.FrameRounding, ret1 = ImGui.SliderFloat("FrameRounding", style.FrameRounding, 0.0, 12.0, "%.0f")
if ret1 then
style.GrabRounding = style.FrameRounding -- Make GrabRounding always the same value as FrameRounding
end
do
local border = (style.WindowBorderSize > 0.0)
local pressed
pressed, border = ImGui.Checkbox("WindowBorder", border)
if pressed then style.WindowBorderSize = (border and default_border_size or 0.0) end
end
ImGui.SameLine()
do
local border = (style.FrameBorderSize > 0.0)
local pressed
pressed, border = ImGui.Checkbox("FrameBorder", border)
if pressed then style.FrameBorderSize = (border and default_border_size or 0.0) end
end
ImGui.SameLine()
do
local border = (style.PopupBorderSize > 0.0)
local pressed
pressed, border = ImGui.Checkbox("PopupBorder", border)
if pressed then style.PopupBorderSize = (border and default_border_size or 0.0) end
end
end
ImGui.SeparatorText("Details")
-- TODO:
ImGui.PopItemWidth()
end
end
local function ShowExampleAppImageViewer()
-- TODO:
end
local ShowExampleAppFullscreen do
local use_work_area = true
local flags = bit32.bor(ImGuiWindowFlags.NoDecoration, ImGuiWindowFlags.NoMove, ImGuiWindowFlags.NoSavedSettings)
function ShowExampleAppFullscreen(open)
local viewport = ImGui.GetMainViewport()
ImGui.SetNextWindowPos(use_work_area and viewport.WorkPos or viewport.Pos)
ImGui.SetNextWindowSize(use_work_area and viewport.WorkSize or viewport.Size)
local ret
open, ret = ImGui.Begin("Example: Fullscreen window", open, flags)
if ret then
_, use_work_area = ImGui.Checkbox("Use work area instead of main area", use_work_area)
ImGui.SameLine()
_, flags = ImGui.CheckboxFlags("ImGuiWindowFlags.NoBackground", flags, ImGuiWindowFlags.NoBackground)
_, flags = ImGui.CheckboxFlags("ImGuiWindowFlags.NoDecoration", flags, ImGuiWindowFlags.NoDecoration)
ImGui.Indent()
_, flags = ImGui.CheckboxFlags("ImGuiWindowFlags.NoTitleBar", flags, ImGuiWindowFlags.NoTitleBar)
_, flags = ImGui.CheckboxFlags("ImGuiWindowFlags.NoCollapse", flags, ImGuiWindowFlags.NoCollapse)
_, flags = ImGui.CheckboxFlags("ImGuiWindowFlags.NoScrollbar", flags, ImGuiWindowFlags.NoScrollbar)
ImGui.Unindent()
if open and ImGui.Button("Close this window") then
open = false
end
end
ImGui.End()
return open
end
end
do
local demo_data = ImGuiDemoWindowData()
local no_titlebar = false
local no_scrollbar = false
local no_menu = false
local no_move = false
local no_resize = false
local no_collapse = false
local no_close = false
local no_nav = false
local no_background = false
local no_bring_to_front = false
local no_docking = false
local unsaved_document = false
function ImGui.ShowDemoWindow(open)
if demo_data.ShowAppFullscreen then demo_data.ShowAppFullscreen = ShowExampleAppFullscreen(demo_data.ShowAppFullscreen) end
if demo_data.ShowMetrics then
demo_data.ShowMetrics = ImGui.ShowMetricsWindow(demo_data.ShowMetrics)
end
if demo_data.ShowStyleEditor then
demo_data.ShowStyleEditor = ImGui.Begin("ImGui Sincerely Style Editor", demo_data.ShowStyleEditor)
ImGui.ShowStyleEditor()
ImGui.End()
end
local window_flags = 0
if no_titlebar then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoTitleBar) end
if no_scrollbar then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoScrollbar) end
if not no_menu then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.MenuBar) end
if no_move then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoMove) end
if no_resize then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoResize) end
if no_collapse then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoCollapse) end
if no_nav then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoNav) end
if no_background then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoBackground) end
if no_bring_to_front then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoBringToFrontOnFocus) end
if no_docking then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.NoDocking) end
if unsaved_document then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.UnsavedDocument) end
if no_close then open = nil end
if not no_menu then window_flags = bit32.bor(window_flags, ImGuiWindowFlags.MenuBar) end
local ret -- is_visible
open, ret = ImGui.Begin("ImGui Sincerely Demo", open, window_flags)
if not ret then
ImGui.End()
return open
end
local label_width_base = ImGui.GetFontSize() * 12
local label_width_max = ImGui.GetContentRegionAvail().x * 0.40
local label_width = IM_MIN(label_width_base, label_width_max)
ImGui.PushItemWidth(-label_width)
DemoWindowMenuBar(demo_data)
if ImGui.CollapsingHeader("Help") then
ImGui.SeparatorText("ABOUT THIS DEMO:")
end
if ImGui.CollapsingHeader("Configuration") then
end
if ImGui.CollapsingHeader("Window Options") then
if ImGui.BeginTable("split", 3) then
ImGui.TableNextColumn() _, no_titlebar = ImGui.Checkbox("No titlebar", no_titlebar)
ImGui.TableNextColumn() _, no_scrollbar = ImGui.Checkbox("No scrollbar", no_scrollbar)
ImGui.TableNextColumn() _, no_menu = ImGui.Checkbox("No menu", no_menu)
ImGui.TableNextColumn() _, no_move = ImGui.Checkbox("No move", no_move)
ImGui.TableNextColumn() _, no_resize = ImGui.Checkbox("No resize", no_resize)
ImGui.TableNextColumn() _, no_collapse = ImGui.Checkbox("No collapse", no_collapse)
ImGui.TableNextColumn() _, no_close = ImGui.Checkbox("No close", no_close)
ImGui.TableNextColumn() _, no_nav = ImGui.Checkbox("No nav", no_nav)
ImGui.TableNextColumn() _, no_background = ImGui.Checkbox("No background", no_background)
ImGui.TableNextColumn() _, no_bring_to_front = ImGui.Checkbox("No bring to front", no_bring_to_front)
ImGui.TableNextColumn() _, no_docking = ImGui.Checkbox("No docking", no_docking)
ImGui.TableNextColumn() _, unsaved_document = ImGui.Checkbox("Unsaved document", unsaved_document)
ImGui.EndTable()
end
end
DemoWindowWidgets()
DemoWindowLayout()
DemoWindowPopups()
DemoWindowTables()
DemoWindowInputs()
ImGui.End()
return open
end
end
return true -- [Roblox] ModuleScripts must return exactly one value
+4756
View File
File diff suppressed because it is too large Load Diff
+2000
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
if _G.__IMGUI_ENV then setfenv(1, _G.__IMGUI_ENV) end -- [Roblox] shared env, see init.lua
--- ImGui Sincerely WIP
-- (Tables and Columns Code)
--- @type ImGuiContext?
local GImGui
-- Sets local `GImGui` in this file(imgui_tables.lua).
-- This is currently only used in main code `ImGui.SetCurrentContext()`
--- @param ctx ImGuiContext?
function ImGui._SetCurrentContext_Tables(ctx)
GImGui = ctx
end
--- @param str_id string
--- @param columns_count int
--- @param flags? ImGuiTableFlags
--- @param outer_size? ImVec2
--- @param inner_width? float
function ImGui.BeginTable(str_id, columns_count, flags, outer_size, inner_width)
if flags == nil then flags = 0 end
if outer_size == nil then outer_size = ImVec2(0.0, 0.0) end
if inner_width == nil then inner_width = 0.0 end
-- TODO:
return true
end
function ImGui.EndTable()
-- TODO:
end
function ImGui.TableNextColumn()
-- TODO:
end
function ImGui.TablePushBackgroundChannel()
-- TODO:
end
function ImGui.TablePopBackgroundChannel()
-- TODO:
end
return true -- [Roblox] ModuleScripts must return exactly one value
File diff suppressed because it is too large Load Diff
+444
View File
@@ -0,0 +1,444 @@
if _G.__IMGUI_ENV then setfenv(1, _G.__IMGUI_ENV) end -- [Roblox] shared env, see init.lua
--- ImGui Sincerely
-- This is a Lua port of original `imstb_rectpack.h`
-- ALL TABLES IN THIS FILE ARE 1-BASED!
-- Avoids table creation when possible
--- The `prev_link` can be stbrp_context or stbrp_node
--- @diagnostic disable
local bit32 = bit32
local STBRP_SORT = table.sort
local STBRP_ASSERT = assert
local STBRP__NOTUSED = function(_) end
local STBRP__MAXVAL = 0x7fffffff
--- @enum STBRP_HEURISTIC_Skyline
local STBRP_HEURISTIC_Skyline = {
default = 0,
BL_sortHeight = 0,
BF_sortHeight = 1
}
local STBRP__INIT_skyline = 1
--- @enum STBRP__PREV_LINK_TYPE
local STBRP__PREV_LINK_TYPE = {
ACTIVE_HEAD = 0,
NEXT = 1
}
--- @alias stbrp_coord int
--- @class stbrp_rect
--- @field id int
--- @field w stbrp_coord
--- @field h stbrp_coord
--- @field x stbrp_coord
--- @field y stbrp_coord
--- @field was_packed int
--- @return stbrp_rect
--- @nodiscard
local function stbrp_rect()
return {
id = 0,
w = 0,
h = 0,
x = 0,
y = 0,
was_packed = 0
}
end
--- @class stbrp_node
--- @field x stbrp_coord
--- @field y stbrp_coord
--- @field next stbrp_node
--- @return stbrp_node
--- @nodiscard
local function stbrp_node()
return {
x = 0,
y = 0,
next = nil
}
end
--- @class stbrp_context
--- @field width int
--- @field height int
--- @field align int
--- @field init_mode int
--- @field heuristic int
--- @field num_nodes int
--- @field active_head stbrp_node
--- @field free_head stbrp_node
--- @field extra stbrp_node[]
--- @return stbrp_context
--- @nodiscard
local function stbrp_context()
return {
width = 0,
height = 0,
align = 0,
init_mode = 0,
heuristic = 0,
num_nodes = 0,
active_head = nil,
free_head = nil,
extra = {stbrp_node(), stbrp_node()}
}
end
--- @class stbrp__findresult
--- @field x int
--- @field y int
--- @field prev_link? table
--- @field prev_link_type? int
--- @return stbrp__findresult
local function stbrp__findresult()
return {
x = 0,
y = 0,
prev_link = nil,
prev_link_type = nil
}
end
--------------------------
--------------------------
---
--- IMPLEMENTATION SECTION
---
---
--- @param context stbrp_context
--- @param heuristic int
local function stbrp_setup_heuristic(context, heuristic)
if context.init_mode == STBRP__INIT_skyline then
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight or heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight)
context.heuristic = heuristic
else
STBRP_ASSERT(false)
end
end
--- @param context stbrp_context
--- @param allow_out_of_mem boolean
local function stbrp_setup_allow_out_of_mem(context, allow_out_of_mem)
if allow_out_of_mem then
context.align = 1
else
context.align = (context.width + context.num_nodes - 1) / context.num_nodes
end
end
--- @param context stbrp_context
--- @param width int
--- @param height int
--- @param nodes stbrp_node[]
--- @param num_nodes int
function stbrp_init_target(context, width, height, nodes, num_nodes)
for i = 1, num_nodes - 1 do
nodes[i].next = nodes[i + 1]
end
nodes[num_nodes].next = nil
context.init_mode = STBRP__INIT_skyline
context.heuristic = STBRP_HEURISTIC_Skyline.default
context.free_head = nodes[1]
context.active_head = context.extra[1]
context.width = width
context.height = height
context.num_nodes = num_nodes
stbrp_setup_allow_out_of_mem(context, false)
context.extra[1].x = 0
context.extra[1].y = 0
context.extra[1].next = context.extra[2]
context.extra[2].x = width
context.extra[2].y = bit32.lshift(1, 30)
context.extra[2].next = nil
end
--- @param c stbrp_context
--- @param first stbrp_node
--- @param x0 int
--- @param width int
--- @return int, int
local function stbrp__skyline_find_min_y(c, first, x0, width)
local node = first
local x1 = x0 + width
STBRP__NOTUSED(c)
STBRP_ASSERT(first.x <= x0)
STBRP_ASSERT(node.next.x > x0)
STBRP_ASSERT(node.x <= x0)
local min_y = 0
local waste_area = 0
local visited_width = 0
while node.x < x1 do
if node.y > min_y then
waste_area = waste_area + visited_width * (node.y - min_y)
min_y = node.y
if node.x < x0 then
visited_width = visited_width + node.next.x - x0
else
visited_width = visited_width + node.next.x - node.x
end
else
local under_width = node.next.x - node.x
if under_width + visited_width > width then
under_width = width - visited_width
end
waste_area = waste_area + under_width * (min_y - node.y)
visited_width = visited_width + under_width
end
node = node.next
end
return min_y, waste_area
end
--- @param c stbrp_context
--- @param width int
--- @param height int
--- @return stbrp__findresult
local function stbrp__skyline_find_best_pos(c, width, height)
local best_waste = bit32.lshift(1, 30)
local best_x = 0
local best_y = bit32.lshift(1, 30)
local fr = stbrp__findresult()
local best_prev_link = nil
local best_prev_link_type = nil
width = width + c.align - 1
width = width - width % c.align
STBRP_ASSERT(width % c.align == 0)
if width > c.width or height > c.height then
fr.prev_link = nil
fr.prev_link_type = nil
fr.x = 0
fr.y = 0
return fr
end
local prev_link = c
local prev_link_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
local node = c.active_head
while (node.x + width <= c.width) do
local y, waste = stbrp__skyline_find_min_y(c, node, node.x, width)
if c.heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight then
if y < best_y then
best_y = y
best_prev_link = prev_link
best_prev_link_type = prev_link_type
best_x = node.x
end
else
if y + height <= c.height then
if y < best_y or (y == best_y and waste < best_waste) then
best_y = y
best_waste = waste
best_prev_link = prev_link
best_prev_link_type = prev_link_type
best_x = node.x
end
end
end
prev_link = node
prev_link_type = STBRP__PREV_LINK_TYPE.NEXT
node = node.next
end
if c.heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight then
local tail = c.active_head
local node_scan = c.active_head
local prev_link_scan = c
local prev_link_scan_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
while tail and tail.x < width do
tail = tail.next
end
while tail do
local xpos = tail.x - width
STBRP_ASSERT(xpos >= 0)
while node_scan.next and node_scan.next.x <= xpos do
prev_link_scan = node_scan
prev_link_scan_type = STBRP__PREV_LINK_TYPE.NEXT
node_scan = node_scan.next
end
if node_scan then
STBRP_ASSERT(node_scan.next.x > xpos and node_scan.x <= xpos)
local y, waste = stbrp__skyline_find_min_y(c, node_scan, xpos, width)
if y + height <= c.height then
if y <= best_y then
if y < best_y or waste < best_waste or (waste == best_waste and xpos < best_x) then
best_x = xpos
best_y = y
best_waste = waste
best_prev_link = prev_link_scan
best_prev_link_type = prev_link_scan_type
end
end
end
end
tail = tail.next
end
end
fr.prev_link = best_prev_link
fr.prev_link_type = best_prev_link_type
fr.x = best_x
fr.y = best_y
return fr
end
--- @param context stbrp_context
--- @param width int
--- @param height int
--- @return stbrp__findresult
local function stbrp__skyline_pack_rectangle(context, width, height)
local res = stbrp__skyline_find_best_pos(context, width, height)
if res.prev_link == nil or res.y + height > context.height or context.free_head == nil then
res.prev_link = nil
res.prev_link_type = nil
return res
end
local node = context.free_head
node.x = res.x
node.y = res.y + height
context.free_head = node.next
local cur
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
cur = res.prev_link.active_head
else
cur = res.prev_link.next
end
if cur.x < res.x then
local next = cur.next
cur.next = node
cur = next
else
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
res.prev_link.active_head = node
else
res.prev_link.next = node
end
end
while cur.next and cur.next.x <= res.x + width do
local next = cur.next
cur.next = context.free_head
context.free_head = cur
cur = next
end
node.next = cur
if cur.x < res.x + width then
cur.x = res.x + width
end
return res
end
--- @param a stbrp_rect
--- @param b stbrp_rect
--- @return bool
--- @package
local function rect_height_compare(a, b)
if a.h > b.h then
return true
elseif a.h < b.h then
return false
else
return a.w > b.w
end
end
--- @param a stbrp_rect
--- @param b stbrp_rect
--- @return bool
--- @package
local function rect_original_order(a, b)
return a.was_packed < b.was_packed
end
--- @param context stbrp_context
--- @param rects stbrp_rect[]
--- @param num_rects int
--- @return int
local function stbrp_pack_rects(context, rects, num_rects)
local all_rects_packed = 1
for i = 1, num_rects do
rects[i].was_packed = (i - 1)
end
STBRP_SORT(rects, rect_height_compare)
for i = 1, num_rects do
if rects[i].w == 0 or rects[i].h == 0 then
rects[i].x = 0
rects[i].y = 0
else
local fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h)
if fr.prev_link then
rects[i].x = fr.x
rects[i].y = fr.y
else
rects[i].x = STBRP__MAXVAL
rects[i].y = STBRP__MAXVAL
end
end
end
STBRP_SORT(rects, rect_original_order)
for i = 1, num_rects do
rects[i].was_packed = (rects[i].x == STBRP__MAXVAL and rects[i].y == STBRP__MAXVAL) and 0 or 1
if rects[i].was_packed == 0 then
all_rects_packed = 0
end
end
return all_rects_packed
end
return {
context = stbrp_context,
rect = stbrp_rect,
node = stbrp_node,
pack_rects = stbrp_pack_rects,
init_target = stbrp_init_target,
setup_allow_out_of_mem = stbrp_setup_allow_out_of_mem,
setup_heuristic = stbrp_setup_heuristic
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+222
View File
@@ -0,0 +1,222 @@
if _G.__IMGUI_ENV then setfenv(1, _G.__IMGUI_ENV) end -- [Roblox] shared env, see init.lua
--- ImGui Sincerely
-- This is a minimal Lua impl of C-like sscanf, sprintf sub-set
-- Supports:
-- - %x, %X, %nx, %nX (where n is a number)
-- - %d, %nd (where n is a number)
-- - %f, %lf (very limited)
--
-- Returns items_matched instead of EOF on error
local _byte = string.byte
local CHAR_PERCENT = _byte'%'
local CHAR_DOT = _byte'.'
local CHAR_PLUS, CHAR_MINUS = _byte'+', _byte'-'
local CHAR_0, CHAR_9 = _byte'0', _byte'9'
local CHAR_a, CHAR_A = _byte'a', _byte'A'
local CHAR_d = _byte'd'
local CHAR_l = _byte'l'
local CHAR_f, CHAR_F = _byte'f', _byte'F'
local CHAR_x, CHAR_X = _byte'x', _byte'X'
local function isspace(c) return c == 32 or c == 9 end
local function isdigit(c) return c >= CHAR_0 and c <= CHAR_9 end
local function isxdigit(c) return isdigit(c) or (c >= CHAR_a and c <= CHAR_f) or (c >= CHAR_A and c <= CHAR_F) end
local function isdigit_under_base(c, base)
if base == 10 then return isdigit(c)
elseif base == 16 then return isxdigit(c)
end
end
local function hex_digit_to_int(c)
if c >= CHAR_0 and c <= CHAR_9 then return c - CHAR_0 end
if c >= CHAR_a and c <= CHAR_f then return c - CHAR_a + 10 end
if c >= CHAR_A and c <= CHAR_F then return c - CHAR_A + 10 end
end
-- we force this behavior by default
local function clamp_if_overflow(spec, val)
if spec == CHAR_d or spec == CHAR_x or spec == CHAR_X then
if val > INT_MAX then return INT_MAX end
if val < INT_MIN then return INT_MIN end
end
return val
end
--- @param buffer char[]
--- @param buffer_begin int
--- @param format string
--- @param result table # table to store parsed values
local function sscanf(buffer, buffer_begin, format, result)
local p = buffer_begin
local f = 1 -- format_begin
local format_len = #format
local assigned = 0
local items_matched = 0
while f <= format_len do
repeat
if isspace(_byte(format, f, f)) then
while isspace(buffer[p]) do
p = p + 1
end
f = f + 1
break
end
if _byte(format, f, f) ~= CHAR_PERCENT then
if buffer[p] == _byte(format, f, f) then
p = p + 1
f = f + 1
else
return items_matched
end
break
end
-- encountered % in fmt
f = f + 1
-- %%
if _byte(format, f, f) == CHAR_PERCENT then
if buffer[p] == CHAR_PERCENT then
p = p + 1
f = f + 1
else
return items_matched
end
break
end
-- handle optional width
local width = 0
while isdigit(_byte(format, f, f)) do
width = width * 10 + _byte(format, f, f) - CHAR_0
f = f + 1
end
if width == 0 then
width = math.huge
end
local spec = _byte(format, f, f)
local base
local is_float = false
if spec == CHAR_x or spec == CHAR_X then
base = 16
elseif spec == CHAR_d then
base = 10
elseif spec == CHAR_f or spec == CHAR_F then
is_float = true
elseif spec == CHAR_l then
f = f + 1
if _byte(format, f, f) == CHAR_f then
is_float = true -- TODO: distinguish from float?
else
return items_matched
end
else
return items_matched
end
f = f + 1
while isspace(buffer[p]) do
p = p + 1
end
local val
if is_float then
local sign = 1
if buffer[p] == CHAR_PLUS then
p = p + 1
elseif buffer[p] == CHAR_MINUS then
sign = -1
p = p + 1
end
-- inf, nan are not handled
local p_start = p
local int_part = 0
while isdigit(buffer[p]) do
int_part = int_part * 10 + (buffer[p] - CHAR_0)
p = p + 1
end
local frac_part = 0
local frac_div = 1
if buffer[p] == CHAR_DOT then
p = p + 1
while isdigit(buffer[p]) do
frac_part = frac_part * 10 + (buffer[p] - CHAR_0)
frac_div = frac_div * 10
p = p + 1
end
end
if p == p_start then
return items_matched
end
val = sign * (int_part + frac_part / frac_div)
else
local sign = 1
-- optional sign
if base == 10 then
if buffer[p] == CHAR_PLUS then
p = p + 1
width = width - 1
elseif buffer[p] == CHAR_MINUS then
sign = -1
p = p + 1
width = width - 1
end
end
local p_start = p
while width > 0 and buffer[p] and isdigit_under_base(buffer[p], base) do
p = p + 1
width = width - 1
end
if p == p_start then
-- failed to match
return items_matched
end
val = 0
while p_start < p do
local c = buffer[p_start]
p_start = p_start + 1
val = val * base + sign * hex_digit_to_int(c)
local old_val = val
val = clamp_if_overflow(spec, val)
if old_val ~= val then
break
end
end
end
result[assigned + 1] = val
assigned = assigned + 1
items_matched = items_matched + 1
until true
end
return items_matched
end
local function sprintf()
end
return sscanf
+3
View File
@@ -0,0 +1,3 @@
require("imgui.imgui")
require("imgui.backend.imgui_impl_love")
require("imgui.imgui_demo")