Initial commit
This commit is contained in:
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
+2000
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
require("imgui.imgui")
|
||||
require("imgui.backend.imgui_impl_love")
|
||||
require("imgui.imgui_demo")
|
||||
Reference in New Issue
Block a user